Nodes/comfyui_imgtools/Image Denoise (Affinity style) (rholdorf)
ComfyUI Node

Image Denoise (Affinity style) (rholdorf)

Affinity Photo's denoise filter, rebuilt as a ComfyUI node

By rholdorf·Created 8 months ago·Updated 12 days ago· 0
Image Denoise (Affinity style) (rholdorf)
  • image
  • IMAGE
luminance30.0
luminance_detail50.0
luminance_contribution100.0
colours50.0
colours_contribution100.0
levels5

You generate an image and it's great, except the shadows are full of high-ISO grain or the colour is blotchy. The classic move: export, open Affinity Photo, run its Denoise filter, export again. Round trip. Slow. And it drags you out of the graph every single time you do it.

Image Denoise (Affinity style) (rholdorf) exists to end that. It reproduces the control surface of Affinity Photo 2's Denoise filter - the same five 0–100 sliders with an equivalent response curve - as a node right in your workflow. It is not bit-identical to the proprietary original, so don't expect a pixel-perfect match to a specific edit you made there; you'll be re-tuning. But the shape of the tool is exactly what you already know, and you never leave ComfyUI. People have literally been asking for a way to stop doing this cleanup manually in Affinity, and this is one of the answers.

How it works

Under the hood it's an à trous (starlet) wavelet decomposition - undecimated, which is why it doesn't ring on edges the way a decimated DWT can - run on a BT.709 YCbCr conversion. The detail levels get shrunk with the non-negative garrote, a shrinkage function that leaves strong coefficients (your edges) untouched instead of pulling everything down the way soft thresholding would. That's the whole difference between "cleaned up" and "turned to mush."

The clever bit is that luma and chroma use deliberately different noise models. Luma assumes white noise and measures sigma once on the finest level, then propagates it - which keeps coarse-scale structure safe. Chroma re-estimates sigma per level, because colour blotching is spatially correlated and carries almost no energy at the finest scale; under the luma model it would read as "no noise" and remove nothing. There's also a chroma depth cap so the colour pass doesn't start flattening real gradients on small images.

The inputs that matter

Five sliders, all 0–100, plus one depth control:

  • luminance (default 30) - how hard the luma pass denoises. Your main knob.
  • luminance_detail (default 50) - higher keeps more fine texture... and more grain.
  • luminance_contribution (default 100) - blends the filtered luma over the original; lower it to keep some of the original's character.
  • colours (default 50) - chroma noise strength. If your problem is blotchy colour rather than grain, this is the one.
  • colours_contribution (default 100) - same blend idea for the colour pass.
  • levels (optional, default 5, 1–7) - wavelet depth. Auto-capped to the image size, so cranking it past what a small image supports buys you nothing.

The README's starting points are good: 30 / 50 / 100 / 50 / 100 for light grain, 60 / 50 / 100 / 70 / 100 for heavy high-ISO noise. If detail goes mushy, raise luminance_detail before lowering luminance. And contribution = 0 or strength 0 is an exact pass-through, which makes A/B-ing the node in a workflow trivial.

Output is a single IMAGE, so it chains anywhere: before a sharpen, before an upscale, or straight into Save Image. It runs on whatever device the tensor is on - CPU or GPU - at roughly 1–3 s for a 4K image on CPU. Frames in a batch get their own noise estimate, which is exactly what you want when they're unrelated images.

Install

This ships in the rholdorf/comfyui_imgtools pack. Clone it into custom_nodes and restart - no build step, no extra dependencies, it runs on what ComfyUI already ships (torch, numpy, Pillow):

cd ComfyUI/custom_nodes
git clone https://github.com/rholdorf/comfyui_imgtools

Then restart ComfyUI. ComfyUI Manager can pull it too if you search for the pack title. No model downloads, nothing heavy. You'll get the pack's other ten nodes along with it whether you want them or not - it's a small personal pack, not a modular install.

Gotchas

Biggest one: don't confuse this with the sampler's "denoise" strength. That's img2img re-generation - a diffusion pass that can rewrite your subject's face if you're not careful. This is a deterministic pixel filter; it changes noise, not content. That's the whole point: reach for the cheap, instant, deterministic primitive before you burn a generative pass on cleanup.

Also worth knowing: because it isn't bit-identical to Affinity, matching a preset you built there takes a little fiddling - start from the defaults above and you'll land close fast. And on small images the chroma pass is deliberately shallower (that depth cap), so if colour noise persists, don't fight it with levels - raise colours instead.

Categoryrholdorf/image

Inputs (7)

NameTypeDefaultDescription
imageIMAGE
luminanceFLOAT30.00–100
luminance_detailFLOAT50.00–100
luminance_contributionFLOAT100.00–100
coloursFLOAT50.00–100
colours_contributionFLOAT100.00–100
levelsoptINT51–7

Outputs (1)

NameTypeDescription
IMAGEIMAGE