Nodes/ComfyUI CV/CV Draw Connections
ComfyUI Node

CV Draw Connections

Draw the skeleton, not just the joints

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
CV Draw Connections
  • source
  • points
  • edges
  • labels
  • image
  • count
◄thickness2►
◄color(255, 255, 255)►

Why you'd reach for this

CV Draw Points marks joints. A hand or a body isn't a cloud of dots, though - it's dots plus a known wiring diagram, and the wiring is what tells you whether the pose estimator put the thumb where the thumb goes. This node draws that wiring: one line per index pair.

It's the companion to the pack's MediaPipe nodes. CV MediaPipe Hand Pose outputs 21 keypoints per palm, and it also outputs a connections array - an Ex2 list of index pairs naming which of the 21 connects to which. Feed landmarks into points and connections into edges and you get the hand skeleton, drawn by a node that has no opinion about hands at all. Any Ex2 index-pair set works: a Delaunay edge list, a graph you built yourself, K-nearest-neighbour pairs.

How it works

For every row (i, j) it draws an anti-aliased line from points[i] to points[j]. Indices that fall outside the point array are skipped rather than raising, which is what lets you feed it a connections table from a model whose keypoint count differs without the graph dying - the tooltip is explicit that out-of-range indices are skipped, and the count tells you how many actually made it.

The colour comes from a single BGR literal by default, but the optional labels input changes the game: an (E,) integer label per edge gives one colour per edge cluster, using the same deterministic index-to-colour mapping as CV Draw Points. That mapping isn't arbitrary - it walks a colour-vision-deficiency-safe categorical palette, and on a single-channel canvas it falls back to spaced grey levels rather than averaging the colours (averaging a CVD-safe palette collapses every class to nearly the same grey, which is a lovely way to defeat the point).

labels also has to have exactly one entry per edge, or the node raises with the counts in the message. Same for points being None or empty: pass-through, count = 0.

Inputs and outputs that matter

  • points - Nx1x2, the same array you'd give a point-drawing node.
  • edges - Ex2 integer index pairs. This is the input that makes the node useful and the one people forget exists on their detector's output panel.
  • thickness and color - default 2 and white. White on a bright photo is invisible; that's what the colour widget is for, not a bug.
  • labels (optional) - one colour per edge cluster, matching CV Draw Points, so a cluster's dots and its bones agree on colour.
  • image out and count. count is how many edges were genuinely drawn after the index and finiteness checks, so a graph that looks empty but reports count = 20 means your lines are off-canvas or zero-length.

Blanked-out points are handled the same way as the rest of the pack: a non-finite coordinate is read as "no position here", so that edge is skipped and every other edge keeps its index alignment with the label array.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Python ≥ 3.12, a ComfyUI new enough for the V3 node API, or the pack as ComfyUI CV in ComfyUI Manager. Sketch-style drawing like this needs no model and no GPU - it's cv2.line on the CPU.

Common issues

  • Lines drawn to the wrong joints. The edges array and the points array came from different keypoint conventions (one is 21-point hand, the other 17-point body COCO). Index tables aren't portable between schemes; the KB's ControlNet notes on pose keypoints are a good reminder that "keypoints" is several incompatible formats wearing one word.
  • Nothing drawn, count = 0. Edges were empty (a detector that found no hand returns none) or every index was out of range against points.
  • Mask output looks empty. A BGR tuple survives as its blue component only on a one-channel canvas. Use '255'.
  • Overlapping colours. You drew two things on one image in two similar colours. Give the second call different labels, or a different thickness.
  • Contrib build. The pack needs opencv-contrib-python-headless; a plain opencv-python install over it empties the contrib submodules and contrib nodes vanish. tools/repair_opencv_contrib.py diagnoses and repairs.
Categoryimage/CV/points

Inputs (6)

NameTypeDefaultDescription
sourceCOMFY_MATCHTYPE_V3Image or mask to draw on (a copy is made). Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
pointsNPARRAYNx1x2 points the edges index into (or None).
edgesNPARRAYEx2 int index pairs; each row (i, j) connects points[i] to points[j]. None/empty -> passthrough.
thicknessINT21–64Line width in pixels.
colorSTRING(255, 255, 255)Color as a single value (broadcast to all channels) or BGR tuple, e.g. '255' or '(255, 255, 255)'. Shorter tuples are zero-padded; longer tuples are truncated. Ignored when labels are connected.
labelsoptNPARRAY(E,) integer labels: one color per edge cluster.

Outputs (2)

NameTypeDescription
imageCOMFY_MATCHTYPE_V3Same format as the image input.
countINTHow many edges were actually drawn (valid indices).