cv2.edgePreservingFilter
Smooth the skin, keep the eyelashes
- src
- result
A Gaussian blur smears everything, edges included. This filter smooths the flat regions and leaves the boundaries alone, which is the difference between "cleaned up" and "out of focus". If you've ever wanted skin smoother without losing the eyelashes, a noisy render denoised without going plastic, or a photograph calmed down before an upscale, this is the node.
The KB's post-processing doc makes the family distinction worth internalising: Gaussian for softening, edge-preserving for denoising and surface smoothing, and reaching for the wrong one is how you lose the edges you were trying to keep. Consider this the OpenCV entry in that family, next door to bilateralFilter and the pack's own cv2.ximgproc.edgePreservingFilter (a different algorithm with its own look - both are worth twenty minutes of A/B).
How it works, and the two knobs that matter
It's OpenCV's photo-module edge-aware filter: it compares pixels by intensity similarity as well as distance, so a neighbourhood only averages together when it's actually alike.
sigma_s is the spatial extent - how far the filter looks, default 60 and documented as a "range between 0 to 200". It's the strength dial: bump it and the smoothing spreads. Drop it to ~10–15 for a small crop, because 60 on a 200-pixel image is a large neighbourhood and it will start eating real structure.
sigma_r is the range sigma - how different two pixels can be before they're considered "not the same surface", default 0.4, range 0 to 1. Crank it up and the filter stops respecting edges and behaves more like a blur; drop it toward 0.1 and it barely changes anything, which is also the correct setting for subtle skin work.
flags chooses between OpenCV's RECURS_FILTER (the default) and NORMCONV_FILTER. The recursive version is the fast approximate one; the normal-convolution version is the more faithful, slower filter. They give visibly different results, not just different speeds, so if a render comes out looking softer than expected, try the other flag before you blame the sigmas.
The single result output echoes the source's format - IMAGE in, IMAGE out, and it's batch-aware, so a whole IMAGE batch gets processed frame by frame.
Inputs and outputs in one line
src takes an IMAGE, MASK or NPARRAY. OpenCV documents it as an 8-bit 3-channel image, which is the design target; the pack feeds an IMAGE through as uint8 BGR, so that's the case you'll normally be in. Grayscale runs too, and worth knowing if you're filtering a single plane.
What it's genuinely good for
- Portrait cleanup, gently.
sigma_s30–60,sigma_r0.3–0.5. This is the honest "skin smoothing" pass - no diffusion model, no resampling, deterministic, millisecond-scale. It doesn't fix bad lighting or reshape anything, and it shouldn't. - Denoise before upscale. Upscalers amplify whatever noise you feed them. A light edge-preserving pass first is cheaper than a regeneration and doesn't move a single pixel's identity.
- Flash/no-flash pipelines. The pack implements the classic two-image recipe - the flash frame has detail, the ambient frame has colour, and an edge-aware filter is what keeps them from turning into soup. The example workflow
10_ximgproc_edge_aware_filters.jsonis built around exactly that pair. - The "is it doing anything?" test. Because the change is subtle by design, a
cv2.absdiffagainst the original plus a normalize is the way to see it. If the difference isn't visible there, your sigmas are too low.
Installing comfyui_cv
One of ~470 auto-generated raw cv2.* wrappers in bmad4ever/comfyui_cv - GPL-3.0, forked from Gerold Meisinger's opencv-comfyui, with a curated node layer on top. Manager: search ComfyUI CV. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart after. Python ≥ 3.12 and a recent ComfyUI on the V3 node API; one dependency, opencv-contrib-python-headless~=5.0.0.93. Keep the contrib wheel: all four OpenCV distributions share one site-packages/cv2, last install wins, and a plain opencv-python silently strips the contrib submodules and their nodes - tools/repair_opencv_contrib.py --check / --apply handles that.
Common issues
Results look like a blur. sigma_r is too high or the flags are on the recursive filter and the image is small. Lower sigma_r first.
Nothing happens. sigma_s too low relative to the image size, or sigma_r too low. Also check you're previewing after the node and not a cached earlier image.
Slow on large images. Big sigma_s means a big neighbourhood. RECURS_FILTER is the faster flag; NORMCONV_FILTER is the one you pay for.
Mushy texture on foliage or fabric. Edge-aware filters preserve strong edges, not fine texture - they'll happily flatten leaves. Lower sigma_s, or treat high-texture images as a job for bilateralFilter instead.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| src | COMFY_MATCHTYPE_V3 | Input 8-bit 3-channel image. The image output(s) echo this input's format. 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. | |
| flagsopt | COMBO | RECURS_FILTER | Edge preserving filters: cv::RECURS_FILTER or cv::NORMCONV_FILTER |
| sigma_sopt | FLOAT | 60.0000-1e+38–1e+38 | %Range between 0 to 200. Preset to the OpenCV default (60.0). |
| sigma_ropt | FLOAT | 0.4000-1e+38–1e+38 | %Range between 0 to 1. Preset to the OpenCV default (0.4). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |