cv2.convexityDefects
Find the dents — the geometry behind finger counting
- contour
- convexhull
- nparray
What a "defect" is
Take the convex hull of a shape and compare it to the shape itself. Everywhere the actual outline dives inside the hull, there's a concavity - a dent. cv2.convexityDefects finds those dents and reports, for each one: which two hull vertices the dent sits between, the far point of the dent, and how deep it goes.
That's a compact description of shape concavity, and it's the classic basis for the OpenCV hand-gesture demo: hold up a hand, take the hull, and each gap between fingers is a defect of a certain depth. More usefully in a ComfyUI graph, it's a cheap way to characterise a shape beyond its bounding box - a blob with deep defects is not a blob, it's a shape with lobes.
It's a raw wrapper from ComfyUI CV (bmad4ever/comfyui_cv), category image/CV/low-level/cv2 C - and it's one of the fussiest nodes in the pack to wire correctly.
How it works
Two inputs, and they must be a matched pair: the original contour, and the hull of that same contour in index form. cv2.convexHull(points, returnPoints=False) returns indices into the contour array; that's the form convexityDefects needs. Hand it the hull as points instead and you get a failure or nonsense, because it's indexing your contour with those numbers.
The output is an (N,1,4) int32 array - one row per defect, four integers:
- index of the hull segment's start vertex,
- index of its end vertex,
- index of the farthest contour point (the bottom of the dent),
- the depth of the dent at that point.
That fourth value has a quirk worth knowing, and the pack's tooltip doesn't mention it: OpenCV reports it as a fixed-point distance with 8 fractional bits, so divide by 256 to get pixels. Filtering on "depth > 20" when the real scale is 256× that will quietly select everything or nothing. It's also an integer column, so shallow defects round toward zero.
Inputs and outputs that matter
- contour - required, NPARRAY only. The original int32 contour,
(N,1,2). The pack's tooltip is precise: "the ORIGINAL int32 contour". - convexhull - required, NPARRAY only. Hull indices from
cv2.convexHullwithreturnPoints=False. Not points. - nparray - the defects,
(N,1,4)int32.
Where the output goes: CV Reduce Points By Label or CV Pick Value (sorted) for picking the deepest dent, CV Draw Segments if you want to see the segment the defect belongs to, and CV Region Properties if you'd rather have convexity-defect counts as a ready-made shape feature column instead of doing this by hand. That curated node is usually the better answer for "describe this blob".
Installing the pack
Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart ComfyUI. Requires Python ≥ 3.12, a ComfyUI on the V3 node API, and opencv-contrib-python-headless~=5.0.0.93.
Where people get burned
- Hull points instead of hull indices. The most common mistake with this node by a mile. Chain
cv2.convexHullwithreturnPoints=False- that's how the pack's own tooltip describes it and there is no alternative path. - The 256× fixed-point scale. Your depth values will look 256 times bigger than you expect. Divide before you threshold.
- Mismatched contour and hull. Hull indices index that contour array. Recompute the hull after any filtering, resampling, or reordering of the contour points, or the indices point at the wrong vertices - and the results still look like plausible defects.
- Expecting defects on a convex shape. Convex things have none, so an empty output is the correct answer, not an error. Handle the empty array (the pack's own docs are careful about empty-batch inputs elsewhere for this reason).
- Non-int contour points. The tooltip says int32 for a reason. Float contours need a cast first.
- The pack's caveats, brief - and this node is a good example of why they matter. The README declares heavy LLM assistance, an acknowledged overfitting risk, "not recommended for production" without independent review, and no planned updates. It also notes that low-level nodes are auto-generated and uncurated: "expect to handle conversions and edge cases yourself". That's literally this node - the wrapper gives you the raw four-column integer array and a tooltip that omits the fixed-point detail. No Reddit corpus exists for the pack (searching returns nothing), so the OpenCV documentation is your reference, not the community.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| contour | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| convexhull | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |