Blur Mask Outward
A Mask Blur That Doesn't Eat the Thing You Masked
- mask
- mask
- grow_amount
Every inpainting tutorial says the same thing about mask blur: 4 to 12 pixels softens the seam, too little gives you a hard edge, too much bleeds changes into the surrounding image. What the tutorials skip is the arithmetic underneath. A Gaussian blur is symmetric. Run one over a hard mask and it does not just feather the outside - it also drags the mask's own edge inward, so the region you painstakingly covered comes back slightly smaller and slightly grey. On a big mask nobody notices. On a small face, an eye, a hand, a jewel, that eaten edge is exactly the content you were trying to fix.
This node blurs outward only.
How it works
The order of operations is the interesting part, and it is not "grow the mask, then blur it":
- Grow the mask by a computed amount - a square structuring element by default, a diamond if you turn on
tapered_corners. The mask is padded first so the growth has room at the canvas edge. - Blur the grown mask with Pillow's Gaussian blur.
- Crop back to the original canvas and force every pixel that was 1.0 in the original mask back to 1.0.
Step 3 is what makes the promise true: whatever the blur did to the interior, the original fully-covered area comes out bit-identical. The feast is entirely outside the original edge.
The growth amount is not a magic ratio either. With tapered_corners off, the node measures Pillow's actual blur support by blurring a synthetic step edge and finding where attenuation begins, then dilates by exactly that much. With tapered_corners on it binary-searches the smallest diamond dilation that survives the blur intact. That is why the result is stable across radii instead of drifting at 3px and 60px.
Inputs and outputs
mask in, mask out - same shape, same 0-1 float range, so it drops into any mask socket.
The two dials that matter:
- blur_radius (float, 0.1 steps, default 0). Zero is a pass-through clone, so leaving it at 0 costs you nothing but also does nothing. 4-12 is the inpainting sweet spot; the slider goes to 100 if you want a genuinely soft plate.
- outward (default on). Turn it off and you get an ordinary symmetric Gaussian blur - useful as an A/B when you are deciding whether the eaten edge was your problem.
- tapered_corners (default off) controls the shape of the growth: off gives square-shouldered corners, on gives rounded ones. If you are feathering a box-shaped region and the corners look pasted-on, this is the dial.
Typical use: mask a face in a detailer, blur the mask outward by 8, feed it to the sampler. Or take a hand-painted mask before Inpaint Crop and Stitch and soften it so the composite seam disappears.
Install
Part of ComfyUI-Utility-Suite. ComfyUI Manager → search ComfyUI-Utility-Suite → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/tom-m-2020/ComfyUI-Utility-Suite
Restart. This node is one of the few in the pack that genuinely needs the declared dependency: it imports OpenCV for the dilation, so let the Manager finish its install step (or pip install opencv-python-headless yourself). Pillow and numpy already come with ComfyUI. There are no model files. The pack targets the newer V3 node API, so run a current ComfyUI.
Traps
Radius 0 is a no-op, not a subtle setting. If turning the knob produces no visible change, check you actually moved off zero.
Thin masks cannot be saved. The promise is "the original solid area stays solid" - if your mask is a 3-pixel line, there is no solid area worth preserving and a 40px blur just smears it. For line-like masks a symmetric blur is often the better tool.
It is CPU work on a float tensor. Fine for a few frames, slower than you expect if you stack it on a video-length mask batch. Nothing that needs a GPU, but not free.
The output is continuous, not binary. You get a real ramp of intermediate values, which is what a sampler wants. If something downstream rounds or thresholds it (core's Mask Invert does, for instance), that ramp is gone the moment it passes through.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — | |
| blur_radius | FLOAT | 0.00–100 | — |
| tapered_corners | BOOLEAN | false | — |
| outward | BOOLEAN | true | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| mask | MASK | — |
| grow_amount | INT | — |