ComfyUI Node

Matte

Turning a rough mask into an edge-following alpha, with no model

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
Matte
  • image
  • mask
  • alpha_matte
  • foreground_image
◄methodGuidedFilter►
◄trimap_dilation12►
◄eps0.0001►

You've got a mask. It's boxy where it should be hair, it's binary where it should be soft, and it's got a pixel of jitter along the edge that will show the moment you composite it against anything with a different brightness.

Matte refines that into a proper soft alpha. Deliberately, it does this without a learned matting model - it's a guided filter that follows the image's own edges, and it runs in milliseconds on the GPU.

How it works

The guided filter is He et al.'s classic, and the recipe is short enough to state. Inside a window around each pixel:

a = cov(I, p) / (var(I) + eps)
b = mean(p) - a * mean(I)
q = mean(a) * I + mean(b)

I is your image, p is your rough mask, q is the refined matte. Read it as a local linear fit: wherever the image has strong structure (high variance), the fitted slope does the work and the matte snaps to the image's edges. Where the image is flat, variance is low, the slope collapses toward zero and the output falls back to the local mean of your mask - i.e. a blur. That's the behaviour you want, and it's why a guided filter beats a blur-then-threshold approach on hair.

The node restricts the work with a trimap, which is where trimap_dilation comes in. Regions whose entire neighbourhood is ≥ 0.99 stay solidly foreground; ≤ 0.01 stays background; only the "unknown" band between them - within trimap_dilation pixels of the edge - gets refined. Set it to 0 and the mask is preserved as-is. This is both an accuracy trick and a speed one.

eps is the regulariser and it's the one real knob: small values make the matte chase fine image edges (including noise), large values give you something closer to a blurred mask. The default 1e-4 behaves quite differently on scene-linear data than on display-encoded data - the tooltip says so, and it's true, because the variance term scales with your encoding.

Inputs and outputs

Required: image (the guide), mask (rough, 0–1), method (only GuidedFilter is implemented - the earlier builds advertised ViTMatte and RVM and ran this filter for both, which is an instructive bit of pack archaeology), trimap_dilation (default 12, up to 128), eps (default 1e-4).

Outputs, and there are two for a reason: alpha_matte, the refined MASK, and foreground_image, which is the image premultiplied by the matte - RGB × alpha, ready to composite. Getting premultiplication right at the source is the thing everyone forgets to do by hand and then wonders about the bright fringe in the composite.

The mask can be HxW or BxHxW, and it doesn't have to match the image batch: a single mask will be broadcast across a batch, and a mismatched mask size gets bilinearly resized rather than throwing. That last one exists because ComfyUI's Load Image returns a 64×64 mask when an image has no alpha channel, which crashed the node in an earlier version. If you see a suspiciously tiny mask in your graph, that's what's happening, and a resample is the graceful answer.

Installing Radiance

Manager → search Radiance → install → restart → refresh. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

Windows portable users: python_embeded\python.exe for pip. No weights - this is a deterministic filter, which is the entire point. The pack (GPL-3.0, ~147 visible nodes) brings OpenEXR, OpenImageIO, OpenColorIO, diffusers and accelerate with it.

Troubleshooting

Edges are hard, no softness at all. trimap_dilation is 0. It means "preserve my mask", so raise it to 12–32 and rerun.

The matte follows texture that isn't the subject. That's the guided filter doing its job on a busy background. eps up, or better: clean the mask's shape before matting rather than asking the filter to decide what matters.

Fringing on the composite. Check image is in the same encoding as the rest of your comp. If the guide is sRGB-encoded and everything downstream is linear, eps behaves differently and the edge fit is subtly wrong.

Speckled alpha in flat regions. Noise in the guide image becomes structure for the filter. Denoise before matting, or raise eps.

Speed at 4K. The filter is linear in window size, not window area, and the node chunks frames to bound memory - around a dozen frame-sized buffers are live at once. Very large trimap_dilation values still cost; 12–32 covers nearly every real use.

When not to use it. If your subject has no clean alpha to begin with - no rough mask with the subject at all inside it - you need a segmenter first. This refines; it doesn't find. And if you want a learned matte specifically for hair and fur, BiRefNet-class models do that job; they're just several orders of magnitude slower and don't give you the interactive loop this node does.

CategoryFXTD STUDIOS/Radiance/VFX

Inputs (5)

NameTypeDefaultDescription
imageIMAGEGuide image whose edges the matte will follow. Used as given (scene-linear or display-encoded); eps behaves differently for each.
maskMASKRough matte to refine, 0 to 1.
methodCOMBOGuidedFilterGuided filter on the scene-linear image. The only method implemented.
trimap_dilationINT120–128Radius of the unknown trimap band and guided-filter window. Regions whose entire neighbourhood is at least 0.99 stay foreground; those at most 0.01 stay background. Only the unknown band is refined. 0 preserves the mask.
epsFLOAT0.00010.000001–0.1Guided filter regularisation. Small values make the matte follow fine image edges closely; large values give a smoother matte closer to a blurred mask.

Outputs (2)

NameTypeDescription
alpha_matteMASK—
foreground_imageIMAGE—