OpenCV morphologyEx_0
Kill specks, fill holes, and extract edges
- src
- kernel
- dst
- nparray
If you do anything with masks - SAM outputs, depth thresholds, inpaint regions - you know they come out noisy. Stray specks, pinhole gaps, fuzzy edges. morphologyEx_0 wraps cv2.morphologyEx, which is the classic toolkit for fixing exactly that: it erodes and dilates a binary image in combinations that remove specks, close holes, and extract edges. It's the single most practically useful node in this whole auto-generated pack, so it's worth learning its raw edges.
The operations (the op input)
op is a raw INT - there's no dropdown, and that's the pack's most user-hostile decision (the author has literally acked that "we should use COMBO"). The codes:
2= ERODE - shrinks white regions, strips thin protrusions3= DILATE - grows white regions, thickens shapes4= OPEN - erode then dilate: removes white specks/salt noise. The most-used one for masks5= CLOSE - dilate then erode: fills small holes/pepper noise6= GRADIENT - dilate minus erode: extracts edges of shapes7= TOPHAT - src minus open: pulls out small bright details8= BLACKHAT - close minus src: pulls out small dark details9= HITMISS - exact pattern matching; almost never what you want here
The classic mask-cleanup chain is OPEN (kill specks) then CLOSE (fill holes), often with iterations bumped to 2–3 for dirtier masks. Gradient is how you turn a mask into an edge map.
Inputs that matter
src(NPARRAY) - the mask. Grayscale/single-channel is the norm; that's what a mask already is.op- one of the codes above.kernel(NPARRAY) - the structuring element, the shape the operation uses. You build it withgetStructuringElement_0from this same pack (it takes a shape code and a size literal like(5, 5)). Bigger kernel = more aggressive effect.anchor(STRING) - a literal like(-1, -1)for "center of the kernel". Default and usually fine.iterations(INT) - how many times to apply.borderType(INT) - edge handling; 0 is usually fine.borderValue(STRING) - a literal Scalar, e.g.(0, 0, 0, 0). Only matters for constant borders.dst(optionalNPARRAY) - the out-parameter; leave it unconnected.
Output: nparray, which goes back through Nparrays2Image to view.
Literals, the recurring gotcha
anchor and borderValue are typed as strings and parsed with ast.literal_eval. Type them exactly like Python: (-1, -1) or (0, 0, 0, 0). A typo - missing paren, wrong brackets - gives invalid syntax (<unknown>, line 0) and the node won't run.
Installing
Part of geroldmeisinger/opencv-comfyui. Via ComfyUI Manager (search "opencv-comfyui") or:
cd ComfyUI/custom_nodes
git clone https://github.com/geroldmeisinger/opencv-comfyui
restart. Dependency: opencv-contrib-python (usually already present).
Pipeline and troubleshooting
IMAGE → Image2Nparray → morphologyEx_0 → Nparrays2Image → IMAGE. Image2Nparray handles RGB→BGR and only accepts batch_size==1.
- "Can't find the kernel input type" - you need
getStructuringElement_0to make a kernel array; there's no widget for it. invalid syntax (<unknown>, line 0)- check your literal strings:(-1, -1), not(-1, -1.- Mask looks like it's doing nothing - check that
opis a number in the list above, and that your kernel size isn't(1, 1)(a 1×1 kernel is a no-op).
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| src | NPARRAY | — | |
| op | INT | — | |
| kernel | NPARRAY | — | |
| anchor | STRING | — | |
| iterations | INT | — | |
| borderType | INT | — | |
| borderValue | STRING | — | |
| dstopt | NPARRAY | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |