OpenCV filter2D_0
Run any convolution kernel you can dream up
- src
- kernel
- dst
- nparray
Every other blur or sharpen node in the ecosystem is a convenience wrapper around one operation: sliding a small grid of numbers over the image and summing. filter2D_0 is the operation itself. It wraps cv2.filter2D, the general 2D convolution, and it means you can apply any kernel you can construct - not just the Gaussian blurs and Sobel edges that ship as named nodes. Want a box blur with a non-standard size, an emboss, a directional sharpen, a corner detector, a custom 5×5 that nobody else bothered to wrap? This is where you build it.
If you've never met a convolution kernel: it's a small matrix that defines how each output pixel is computed from its neighborhood. A 3×3 all-ones kernel divided by 9 averages neighbors (blur). A kernel that's -1 on the left column, 0 center, +1 right is a horizontal edge detector. The kernel is the recipe; filter2D is the oven. The community post-processing layer runs on exactly these primitives - sharpening is literally blur-and-difference (unsharp masking), and that's a kernel recipe too.
The inputs that matter
src(NPARRAY) - the image, BGR 0–255.kernel(NPARRAY) - the recipe. This is a numpy array, and it's the same friction as every nparray input in this pack: you need the array to come from somewhere. The pack shipsgetStructuringElement_0(MORPH_RECT/ELLIPSE/CROSS kernels -shape0/1/2,ksizeas a literal like(3, 3)), which hands you a ready-made kernel to drop straight in.getGaussianKernelgives you a 1-D column; combine two for a full Gaussian.ddepth(INT) - output depth.-1means "same as input," which is what you want for BGR uint8 and all the common filters. Other values get you into float depths, which you'll need for some kernel results (Sobel-type gradients can go negative).anchor(STRING) - the kernel's reference point, as a literal tuple:(-1, -1)means center. Just type(-1, -1).delta(FLOAT) - a constant added to every output pixel after the convolution. Nice for brightening a filter pass; 0 is fine.borderType(INT) - how edges are handled: 0 constant, 1 replicate, 2 reflect, 4 is OpenCV's default (reflect_101). Start at 4.dst(NPARRAY, optional) - out-parameter; leave unplugged.
Output: one nparray, the filtered image. Through Nparrays2Image to get back a Comfy IMAGE.
Installing it
Part of the ~600-node geroldmeisinger/opencv-comfyui pack. ComfyUI Manager → search opencv-comfyui, or:
cd ComfyUI/custom_nodes
git clone https://github.com/geroldmeisinger/opencv-comfyui
then restart. Needs opencv-contrib-python, numpy, torch. If startup throws Cannot import name 'guidedFilter' from 'cv2.ximgproc', conflicting OpenCV installs - consolidate.
And the house rules: nparrays in BGR 0–255 (Image2Nparray in, Nparrays2Image out), batch size 1.
Where people get stuck
The kernel. filter2D_0 is only as useful as the array you can feed it, and hand-typing a kernel inside ComfyUI is awkward - there's no "type 9 numbers here" widget. The clean path is getStructuringElement_0 for standard shapes, or a sibling node that emits a matrix. It's also easy to forget the output depth: with ddepth at -1 on a uint8 image, results that should be negative get clipped to black, which makes edge-detection kernels look wrong. If a filter "does nothing" or "turns black," set ddepth to a float depth and check anchor and borderType. The _1 twin is identical in the UI - no reason to prefer it.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| src | NPARRAY | — | |
| ddepth | INT | — | |
| kernel | NPARRAY | — | |
| anchor | STRING | — | |
| delta | FLOAT | — | |
| borderType | INT | — | |
| dstopt | NPARRAY | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |