Nodes/ComfyUI CV/cv2.ximgproc.l0Smooth
ComfyUI Node

cv2.ximgproc.l0Smooth

For when you want flat regions with genuinely sharp edges

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
cv2.ximgproc.l0Smooth
  • src
  • result
◄lambda_0.0200►
◄kappa2.0000►

Every other smoother in this module asks "how big is this difference?" L0 smoothing asks a different question: how many non-zero gradients does the output need? It's a sparsity problem - minimise the count of locations where neighbouring pixels differ at all - and the result looks like a cartoon: big flat plateaus and hard steps between them, not the soft ramps a blur gives you. That's the whole character of the node, and the reason it's the one to reach for when "smooth" means "posterise-ish but keep the drawing", not "slightly less noisy".

The pack's own filter playground uses it as the stylisation-flavoured member of the edge-aware family, right next to pencilSketch, stylization and edgePreservingFilter.

How it works

It's an iterative solver on a global objective, with the smoothness term split so that a gradient either matters (it's a real edge, and gets a weight) or doesn't (it's noise, and is flattened to zero). In practice you're trading number-of-surviving-edges against how much softness is allowed around the ones that survive.

Two optional inputs, both worth touching:

  • lambda_ (0.02, the OpenCV default) - smoothing strength, i.e. how expensive it is to keep an edge. Larger flattens more and leaves fewer edges standing. The pack's examples run 0.015–0.02; the interesting range for stylisation goes higher (0.05, 0.1) if you want real flatlands.
  • kappa (2.0) - the convergence factor of the solver. Larger converges faster but coarser. The author's tooltip recommends leaving it at 2.0, and that's good advice: it's an implementation detail, not an aesthetic knob.

Output is a single result that echoes src's format. Feed it an IMAGE and you get an IMAGE, which makes it unusually pleasant to experiment with - you can chain it straight into the rest of a graph or a preview with no bridge nodes.

The trick that makes it worth learning

Because the output is flat inside regions and sharp at boundaries, subtracting the smoothed version from the original hands you every texture and every bit of grain, isolated from the structure. Add that detail back at reduced strength and you've built a "reduce texture, keep the drawing" pass - which is a much better answer to "make this photo look more illustrative" than a bigger denoise. The pack's workflow does exactly this pairing, with a preview node titled "detail layer (put it back to sharpen)".

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

Restart, or install ComfyUI CV from ComfyUI Manager. Requires Python ≥3.12, a recent ComfyUI on the V3 node API, and opencv-contrib-python-headless~=5.0.0.93 - the pinned version the pack's behaviour is curated against, no model files involved. Contrib-only: if some other node pack has left a plain opencv-python wheel on top, the ximgproc nodes disappear from the menu; tools/repair_opencv_contrib.py --check names it, --apply fixes it.

Common issues

"It did nothing", or "it ate my image". Both are lambda_. At low values you keep the noise; far too high and you get a two-tone poster. Sweep it rather than trusting the default.

Band edges look like stains. L0 smoothing produces visible region boundaries on smooth gradients (skies, walls, skin) because a slow ramp eventually has to become a step somewhere. If that's unacceptable, guidedFilter or rollingGuidanceFilter are the honest alternatives in this same module - this is the family where the filters genuinely disagree, and the pack's demo workflow exists precisely to let you compare them side by side.

It's slower than it looks. The node runs in-process, and the solver is iterative, so on a 4K frame it's seconds. Filter at preview resolution while you're tuning, then run the settings once at full size.

No hole awareness. Same caveat as every filter here: an invalid-pixel marker in a disparity or depth map is just another value to be averaged.

Categoryimage/CV/low-level/ximgproc

Inputs (3)

NameTypeDefaultDescription
srcCOMFY_MATCHTYPE_V3source image for filtering with unsigned 8-bit or signed 16-bit or floating-point depth. 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.
lambda_optFLOAT0.0200-1e+38–1e+38 - - - Preset to the OpenCV default (0.02).
kappaoptFLOAT2.0000-1e+38–1e+38parameter defining the increasing factor of the weight of the gradient data term. For more details about L0 Smoother, see the original paper . Preset to the OpenCV default (2.0).

Outputs (1)

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