CV Merge Contours
Put a broken outline back together
- image
- contours
- contours
- count
- labels
Segmentation edges are never clean. Threshold a logo off a busy background and the ring comes back as four arcs. GrabCut a person and their arm detaches from the torso by three pixels. Now your "one object" is six contours, and every downstream node - contourArea, a shape filter, a min-area rect, a crop - is answering a question about a fragment.
This node takes the contour set and merges contours whose boundaries come within gap pixels of each other, into one contour per group. It's the repair step between "the mask is basically right" and "the measurement is meaningful."
How it works, and the two modes
image supplies only the canvas - its width and height, nothing else. It accepts IMAGE or MASK (frame 0) directly, or an NPARRAY. contours comes from CV Find Contours. The merge groups by boundary-to-boundary proximity: closer than gap pixels (default 10, in the canvas's pixel units) and you're in the same group.
Then mode decides what a group becomes, and the two options are genuinely different objects:
trace merged outline (close gaps)- draws the group's contours into a mask, closes the gaps morphologically, and re-traces the union. The result follows the original shapes, including concavities. This is the one you want for maintaining a real outline.convex hull per group- spans all member points with a hull. Simpler, much faster, and strictly convex: an L-shaped bracket comes back filled in, which may be fine (a bounding context) or completely wrong (an outline you meant to keep).
Outputs: contours (one per group, sorted by area largest-first, matching CV Find Contours' own convention so downstream expectations hold), count, and labels - an int32 group id per input contour, in input order. That last one is the debugging output: when something merged that shouldn't have, labels tells you exactly which inputs went together, and you turn gap down.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Manager → search ComfyUI CV (author bmad4ever). Restart ComfyUI and reload the page. Requires Python ≥ 3.12 and a recent ComfyUI on the V3 node API; the only dependency is opencv-contrib-python-headless~=5.0.0.93. No models.
Common issues
gap is in pixels, and resolution changes what it means. 10 pixels on a 512-crop is a different instruction than 10 pixels on a 4K plate. Scale it with your image or you'll merge objects that are visually distinct but numerically close.
Everything merged into one blob. A too-large gap, or two objects that legitimately touch (a person holding a bag). Check labels and count before assuming the merge logic is wrong.
Contours that should have merged, didn't. The gap is boundary-to-boundary, so contours that share a bounding-box region but are far apart along their actual paths stay separate. That's usually correct behaviour and occasionally surprising.
A mask-input confusion. image is the canvas, not the thing being merged - a classic misread on first use, since it looks like it should matter more than it does. Any frame with the right dimensions will do.
Worth flagging for context, since this pack ships ~470 auto-generated raw cv2.* wrappers alongside its curated nodes: this is one of the curated ones, a composition rather than a single cv2 call. The pack's README is explicit that the generated wrappers are uncurated and you handle edge cases yourself, so when picking nodes, prefer the hand-written ones unless you specifically need a raw function. They're listed in docs/custom_nodes.md in the repo.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | NPARRAY,IMAGE,MASK | Only its width/height are used (the canvas the contours live on). 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 | From 'CV Find Contours'. | |
| gap | FLOAT | 100–10000 | Contours closer than about this many pixels are merged into one. |
| mode | COMBO | trace merged outline (close gaps) | How each group becomes one contour: bridge the gaps and re-trace (keeps the shapes), or take the convex hull of all member points. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| contours | CV_CONTOURS | One merged contour per group, sorted by area. |
| count | INT | — |
| labels | NPARRAY | int32 group id per INPUT contour (same order as the input set) - which inputs were merged together. |