Nodes/ComfyUI CV/CV Draw Labels
ComfyUI Node

CV Draw Labels

Name your keypoints so debugging isn't a guessing game

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Draw Labels
  • source
  • points
  • labels
  • image
  • count
◄font_scale0.4►
◄color(0, 255, 255)►
◄thickness1►
◄offset_x4►
◄offset_y-4►
◄outlinetrue►

Why you'd reach for this

A numbered dot cloud is fine until it isn't. The moment you care which dot is the wrist and which is the fingertip - or which detection score belongs to which box, or which QR code said what - you need text next to the point.

That's the gap this fills. ComfyUI core's Draw BBoxes can label COCO-style boxes; it can't caption an arbitrary point set. This node labels N points with N strings, in order.

How it works

It walks the point array and stamps a label next to each one with cv2.putText, offset by offset_x and offset_y (defaults 4 and −4, i.e. up and to the right of the point), scaled by font_scale, in your colour at your stroke width.

Leave labels unconnected and each point is captioned with its own index - 0, 1, 2 - which is honestly the mode you'll use most, because it's how you find out the order of an array you didn't produce. (It's also how you spot that a MediaPipe hand node numbers its 21 keypoints in a specific order rather than the order you assumed.)

Wire labels and you get real text. The important detail: it's an array of strings or numbers, one per point, not a comma-separated string. CV Text To Array types one per line and produces exactly that; CV Numbers To Array does the numeric version; and several detectors already emit usable text arrays - a QR or barcode detector's texts, CV Load Labels' array, a classifier's label output. If the array length doesn't match the point count, the node raises with both counts, which is a much better outcome than silently mislabelling everything.

outline defaults on, and it's not decoration. These labels land on whatever the photograph happens to show, and a flat single-colour label at font scale 0.4 is often illegible over texture. With it on, the node picks a contrasting halo - black behind a light label, white behind a dark one - by looking at your foreground colour. Turn it off if you're compositing text onto a flat synthetic canvas.

Points with a non-finite coordinate are skipped without renumbering the rest: the pack's convention is that NaN means "no position for this item", not "an error". That's what lets you blank entries out of a gated array and keep the labels aligned with the surviving rows.

Inputs and outputs that matter

  • points - Nx1x2. None or empty passes the image through, count = 0.
  • font_scale - 0.4 by default. Shrink it for a dense set; a labelled 21-point hand wants about that, a labelled 500-point cloud wants almost nothing.
  • color, thickness - the text itself.
  • labels (optional) - the array described above. The single most useful optional input in this section of the pack.
  • offset_x / offset_y - nudge labels off the dots. With a big marker underneath, 4/−4 isn't always enough.
  • outline - the halo.
  • image and count. The count tracks the point array you fed in rather than the labels that survived, so a blank canvas reporting a non-zero count means the points are off-canvas, stacked, or blanked out - not that the node failed.

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 and a current ComfyUI (V3 node API), or install the pack as ComfyUI CV from Manager. CPU drawing only.

Common issues

  • Labels unreadable. Raise font_scale and make sure outline is on; also check color isn't a BGR tuple on a single-channel MASK, where only the blue component survives.
  • All labels say "0". You wired a single text value where an array belongs. Use CV Text To Array.
  • Count mismatch. Array length ≠ point count. They describe the same set, so they have to be built together.
  • Text overflows the frame. Points near the right edge with a positive offset_x. Negative offsets or a smaller font.
  • Contrib trap. The pack needs opencv-contrib-python-headless; installing a plain opencv-python over it empties the shared contrib submodules and contrib nodes vanish. tools/repair_opencv_contrib.py --check, then --apply.
Categoryimage/CV/points

Inputs (9)

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 to label (or None -> passthrough).
font_scaleFLOAT0.40.1–8cv2 text scale; shrink it for dense point sets.
colorSTRING(0, 255, 255)Color as a single value (broadcast to all channels) or BGR tuple, e.g. '255' or '(0, 255, 255)'. Shorter tuples are zero-padded; longer tuples are truncated.
thicknessINT11–32Text stroke width in pixels.
labelsoptNPARRAY(N,) ARRAY of strings or numbers, one per point - not a comma-separated string. Type them with 'CV Text To Array' (one per line), or wire an array that already exists: a QR/barcode detector's 'texts', 'CV Load Labels'.labels_array, a classifier's 'labels_out', or any numeric array via 'CV Numbers To Array'. Leave it unwired to caption each point with its INDEX, which is also what a point whose position is NaN keeps - blanked points are skipped without renumbering the rest.
offset_xoptINT4-4096–4096Horizontal pixel offset of the text from its point.
offset_yoptINT-4-4096–4096Vertical pixel offset of the text from its point.
outlineoptBOOLEANtrueDraw a contrasting halo behind each label (black behind a light 'color', white behind a dark one). These labels land on whatever the image happens to show, so a single flat colour at font_scale 0.4 is often unreadable without it.

Outputs (2)

NameTypeDescription
imageCOMFY_MATCHTYPE_V3Same format as the image input.
countINTHow many labels were drawn.