CV Draw Contours
Make Find Contours stop lying to you
- source
- contours
- image
Why you'd reach for this
Contours are a set of arrays and the arrays always look plausible. Whether they follow the shape you care about - the subject instead of the wallpaper, one blob instead of four hundred - is a visual question, and this node answers it. It's also the fastest way to tell whether the threshold upstream of CV Find Contours needs work before you start tuning anything downstream.
How it works
It slices the contour set to max_draw if you set one - and because CV Find Contours emits contours sorted by area, largest first, max_draw = 1 draws the biggest blob in the frame. That ordering is the whole reason "at most N" is a useful control rather than an arbitrary one.
fill on, or line_style = solid, and it goes through cv2.drawContours with thickness -1 or your thickness. Any other line style takes a different path: cv2 has no stroke pattern at all, so the pack walks the polyline by arc length and stamps short solid segments, keeping positions in float until each dash is placed so the pattern doesn't drift along a long edge. Same trick as the pack's other line nodes, and it exists for a reason the author states flatly - colour alone can't separate two overlaid line sets for a colour-blind viewer or in greyscale, so give the second contour set a dashed style and the picture reads without relying on the palette.
Output format mirrors the input: IMAGE in, IMAGE out; MASK in, MASK out.
Inputs and outputs that matter
- contours - a
CV_CONTOURSset, i.e. the output ofCV Find Contours, or a filtered/selected set fromCV Filter Contours,CV Select Contour,CV Merge Contours. - thickness - ignored when
fillis on.-1isn't needed here; the boolean is the switch. - fill - turn it on to check what a contour actually encloses. Filling a contour derived from a ring-shaped mask is the classic way to discover it has a hole you didn't expect.
- max_draw - 0 means all. On a noisy threshold that's thousands of contours and a black canvas; set it to 10 and you can see what the shape of the top ten is.
- color - the stroke colour, as a bare number (broadcast to all channels) or a BGR tuple. On a one-channel MASK only its blue component survives.
- line_style (optional) -
solid,dashed,dotted,dash-dot. Ignored when filling. - image - the only output. Draw, then look; there's no
counthere, so if you need "was there anything",CV Find Contoursalready gives you the set size.
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 recent ComfyUI (the pack is built on the V3 node API), or install ComfyUI CV via ComfyUI Manager. Pure CPU cv2 drawing; no model, no VRAM.
Common issues
- Nothing appears on a MASK. A BGR tuple on a single-channel canvas keeps only its blue component, so the default
'(0, 255, 0)'becomes 0 and draws nothing visible. Use'255'. - A solid black (or white) image.
max_drawat 0 with a bad threshold: thousands of contours overlapped into a filled mess. Cap it and fix the threshold upstream. - Only the biggest blob is drawn. That's
max_draw = 1doing exactly what it says. Raise it. - Contours drawn thin against the actual shape. Contours sit on the boundary pixels, so a 1-pixel outline over a bright subject is genuinely hard to read; 2–3 pixels and a contrasting colour is the fix, not a code problem.
- Contrib trap. Installing plain
opencv-pythonover the contrib wheel silently empties the contrib submodules and takes the pack's contrib-backed nodes with it.tools/repair_opencv_contrib.py --checktells you,--applyfixes it.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| source | COMFY_MATCHTYPE_V3 | Image or mask to draw on (a copy is made; the input is not modified). 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. | |
| contours | CV_CONTOURS | Contours to draw, e.g. from 'CV Find Contours'. | |
| color | STRING | (0, 255, 0) | Color as a single value (broadcast to all channels) or BGR tuple, e.g. '255' or '(0, 255, 0)'. Shorter tuples are zero-padded; longer tuples are truncated - so on a single-channel MASK canvas a BGR tuple keeps only its BLUE component ('(0, 255, 0)' becomes 0, i.e. draws nothing visible). Use a single value like '255' for a MASK. |
| thickness | INT | 21–64 | Outline width in pixels (ignored when 'fill' is on). |
| fill | BOOLEAN | false | Fill the contours instead of outlining them. |
| max_draw | INT | 00–10000 | Draw at most this many contours (largest first, as sorted by 'CV Find Contours'). 0 = all. |
| line_styleopt | COMBO | solid | Stroke pattern. Colour alone cannot separate two overlaid line sets for a colour-blind viewer or in greyscale - give the second set a different style and the picture reads without the palette. 'solid' is cv2's own line. Ignored when 'fill' is on. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | COMFY_MATCHTYPE_V3 | Same format as the image input. |