Nodes/ComfyUI CV/CV Skeletonize
ComfyUI Node

CV Skeletonize

Turn a fat blob into a one-pixel centerline

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Skeletonize
  • image
  • skeleton
◄methodthinning (Zhang-Suen)►

A thick shape is awkward to measure. Its centroid is fine, but its orientation, its branches, its length - all of that wants a line, not a blob. CV Skeletonize reduces a binary mask to a 1-pixel-wide centerline, which is the step that turns "a dark smudge pointing roughly northwest" into "a ray with a measurable angle".

The author's own example is a clock reader: thick hands in, one-pixel rays out, then measure the angle.

The two methods, and the dependency trap

  • thinning (Zhang-Suen) - the default. Clean, connected centerlines.
  • thinning (Guo-Hall) - the alternative thinning algorithm, same family, slightly different results at junctions.
  • morphological - the fallback. Works everywhere, noisier.

The first two are cv2.ximgproc.thinning, which lives in opencv-contrib. That's the whole reason the method dropdown has three entries instead of two: if the contrib build isn't there, the node degrades to the morphological skeleton and prints a one-time warning to the console naming the missing API, rather than throwing AttributeError and halting your workflow. That failure-tolerance is deliberate, and if you've ever had a graph die because a submodule moved, you know why it's the right call.

The morphological path is a loop of open / subtract / erode until nothing's left. It's the same idea implemented crudely; expect spurs and lumpier junctions. Fine for "which way is this thing pointing", not fine for skeleton graph topology.

So: if you see the warning in your console, your results quietly changed method. That's the single most important thing on this page. The node keeps working, which means nothing looks broken.

What you set

Two inputs, and that's it:

  • image - a binary image or a MASK, non-zero = foreground. Takes a ComfyUI IMAGE/MASK (frame 0 of a batch) or an NPARRAY, and returns the skeleton in whatever format you gave it - MASK in, MASK out. The pack's arithmetic nodes will process a whole IMAGE batch where inputs match in batch size, so batching is possible without leaving the CV nodes.
  • method - the dropdown above.

One output: skeleton, same format as the input.

Where it goes in a graph

Into CV ShapeMoments to get an orientation and a pose confidence for the ray - which is exactly the clock-reading pattern, and worth knowing that pose_confidence exists before you trust an angle off a nearly-symmetric hand. Into CV Find Contours when you want the skeleton as a contour. Into CV DetectLineSegments if the shape really is line-like under the noise. Or into CV Draw Points / a preview to eyeball whether the thinning looks sane before you build thresholds on it - always do this once, because skeleton quality is where the difference between thinning and morphological fallback becomes visible.

Worth flagging the practical limit: skeletonization is a measurement step, not a generative one. The KB's post-processing framing applies - this is determinism doing a job you'd otherwise be tempted to hand to a model. A one-pixel centerline is a gamma-curve-style primitive: instant, free, and precise, and no diffusion pass will beat it at measuring where a line points.

Install

ComfyUI Manager → ComfyUI CV → install → restart. Manual:

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, recent ComfyUI on the V3 node API, no models. The contrib part of that wheel is exactly what the good skeleton needs - which brings us to the failure mode this pack is most prone to.

Common issues

  • Console says cv2.ximgproc.thinning is unavailable ... falling back. You're on the morphological method without having chosen it. Fix the OpenCV install, don't just accept the result:
    python tools/repair_opencv_contrib.py --check
    python tools/repair_opencv_contrib.py --apply
    The usual cause is a non-contrib wheel (opencv-python, opencv-python-headless) installed by some other custom node - all four OpenCV distributions share one site-packages/cv2, so the plain one empties the contrib submodules and contrib-backed nodes silently change behaviour or disappear entirely.
  • Skeleton looks like a spider, with spurs off the main line. That's the morphological method, and no parameter will fix it. Get contrib working.
  • Skeleton is empty. Input mask has no foreground under the threshold, or it's inverted. The node doesn't error on an empty mask, per the pack's house style.
  • Your mask came from an IMAGE batch and only one frame changed. These nodes take frame 0 of a batch unless you keep everything inside the CV ecosystem, which processes full batches where the arithmetic nodes can.
Categoryimage/CV/contours

Inputs (2)

NameTypeDefaultDescription
imageCOMFY_MATCHTYPE_V3Binary image (or MASK); non-zero = foreground. The skeleton comes back in this input's format. 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.
methodCOMBOthinning (Zhang-Suen)Zhang-Suen / Guo-Hall need opencv-contrib-python(-headless) (cv2.ximgproc) and give the cleanest centerlines; if that build is unavailable they fall back to 'morphological' automatically. Morphological works everywhere but is noisier.

Outputs (1)

NameTypeDescription
skeletonCOMFY_MATCHTYPE_V3Same format as the image input.