Nodes/ComfyUI CV/cv2.bilateralFilter
ComfyUI Node

cv2.bilateralFilter

Smooth the skin, keep the edges, pay the clock

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.bilateralFilter
  • src
  • result
◄d0►
◄sigmaColor0.0000►
◄sigmaSpace0.0000►
◄borderTypeBORDER_DEFAULT►

A Gaussian blur smears everything, edges included. A bilateral filter weights neighbours by colour similarity as well as distance, so it smooths flat regions and refuses to cross a boundary. Same softening, no mush - which is why it's the classic skin-smoothing and denoising filter and why it's been in every photo tool since the 90s.

It's the filter to reach for when a normal blur destroys exactly the detail you were trying to keep: line art, portrait skin, an edge you're about to detect.

How it works

Each output pixel is a weighted average of its neighbourhood, where each neighbour's weight falls off with spatial distance (sigmaSpace) and with how different its colour is from the centre (sigmaColor). A pixel across a high-contrast edge gets a colour weight near zero and contributes nothing. That's the entire mechanism, and it explains both knobs.

d is the neighbourhood diameter. At 0 it's derived from sigmaSpace, and since cost scales with roughly d², keeping d small is how you keep this affordable - d = 5 or 9 with sigmaSpace ≈ 1.5 × d is the standard pairing.

sigmaColor is the one that decides what the filter does. Small (10–25) cleans up mild sensor noise. Medium (50–75) is the usual "smooth skin, keep features" setting. Large (150+) mixes across everything and flattens the image into a poster - which is exactly what you want for the cartoonify look, and exactly what you want to avoid when you're denoising.

Worth knowing: bilateralFilter accepts 1- or 3-channel images and that's it - 4 channels raise. And the pack excludes it from its latent-safe set for precisely that reason (cv2 caps it at 1/3 channels), so it's a picture-space tool. On the batch front it runs frame-by-frame, so a video batch won't crash it, it'll just take a while.

Inputs and outputs

  • src (COMFY_MATCHTYPE_V3) - 8-bit or float, 1 or 3 channels. Decides the output format.
  • d (INT) - neighbourhood diameter; non-positive means "derive it from sigmaSpace".
  • sigmaColor (FLOAT) - how far apart in colour two pixels can be and still blur together.
  • sigmaSpace (FLOAT) - how far apart in space.
  • borderType (optional, default BORDER_DEFAULT).
  • result - echoes src.

What it's good at

  • Portrait smoothing without plasticking the face. Bilateral keeps the eyelash and lip boundaries while evening out skin texture; a Gaussian removes both. The post-processing layer's rule of thumb - Gaussian to soften, bilateral to smooth while preserving structure - is the right one.
  • Denoising before edge detection. Kill the noise, keep the edges you're about to find. The pack's cartoonify and clock-reading workflows both lean on it.
  • Line-art cleanup. Smooth the paper, keep the strokes.
  • The "surface blur" aesthetic where you want flat regions and crisp outlines.

If you're denoising video, note what else is on the shelf in this pack: cv2_ximgproc_jointBilateralFilter (a guide image drives the smoothing), cv2_ximgproc_guidedFilter, and cv2_ximgproc_dtFilter are all in the same family and often a better fit. And a README-sourced warning that generalises to this corner of OpenCV: ximgproc.fastBilateralSolverFilter is exposed by this pack but raises (-213) needs to be compiled with EIGEN on the pinned reference build. Recipes that look good in a paper are not all runnable in the shipped wheel.

Install

ComfyUI Manager → comfyui_cv (ComfyUI CV), or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Restart ComfyUI. Python ≥ 3.12 and a recent V3-API ComfyUI; behaviour is curated against OpenCV 5.0.0.93. Keep the contrib wheel - installing plain opencv-python on top of it silently empties the contrib submodules.

Where people get burned

It's slow, and that's expected. The naive implementation is O(N·d²) with a per-neighbour colour weight. d = 25 on a 4K frame is a coffee break. Drop d, or try the ximgproc variants.

sigmaColor too high. Everything flattens and you blame the node for making your image look like a cartoon. That's the setting working as designed.

Feeding 4-channel data. RGBA raises. Take the alpha off first.

Expecting it to work in latent space. It won't - cv2 restricts it to 1/3 channels, so the pack deliberately keeps it off the latent-safe list. For smoothing a latent, cv2_GaussianBlur is the one that works there.

Halo/patchiness at high settings. The filter is not edge-aware globally: it can leave stair-stepped transitions between the smoothed and preserved regions. Reaching for the guided/joint-bilateral filters, which use a guide to decide where the edges are, is the usual next step.

Categoryimage/CV/low-level/cv2 B

Inputs (5)

NameTypeDefaultDescription
srcCOMFY_MATCHTYPE_V3Source 8-bit or floating-point, 1-channel or 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.
dINT0-2147483648–2147483647Diameter of each pixel neighborhood that is used during filtering. If it is non-positive, it is computed from sigmaSpace.
sigmaColorFLOAT0.0000-1e+38–1e+38Filter sigma in the color space. A larger value of the parameter means that farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting in larger areas of semi-equal color.
sigmaSpaceFLOAT0.0000-1e+38–1e+38Filter sigma in the coordinate space. A larger value of the parameter means that farther pixels will influence each other as long as their colors are close enough (see sigmaColor ). When d>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is proportional to sigmaSpace.
borderTypeoptCOMBOBORDER_DEFAULTborder mode used to extrapolate pixels outside of the image, see #BorderTypes

Outputs (1)

NameTypeDescription
resultCOMFY_MATCHTYPE_V3Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY.