Dilate Mask for Each Face
One Clean Mask per Face, Sized to That Face
- mask
- segs
- MASK
Multi-face scenes wreck most mask pipelines. You get one big mask blob, or a mask where every face bleeds into its neighbor, and then your inpaint pass "fixes" two faces at once. This node takes your mask plus the SEGS stream that found the faces, and produces a per-face mask - each face gets its own masked region, cropped to that face's bounds and shrunk by an amount proportional to the face's size.
It's part of ComfyUI-off-suite, and it's honestly one of the more genuinely useful nodes in the pack for anyone doing face-level inpaint or detail work on group photos.
How it works
Feed it three things:
- mask - your face mask (from a SAM, a bbox-to-mask node, whatever).
- segs - the SEGS stream from an Impact Pack-style detector, so the node knows each face's crop region.
- dilation_factor - default
1.0, range 0–10.
For each face it then: zeroes out everything in the mask outside that face's crop region, then runs a kernel operation on what remains. Here's the honest bit - the node is named "Dilate" but the code calls cv2.erode. It shrinks each face's mask, it doesn't grow it. The kernel size scales with the face area, so a big face gets a bigger shrink than a small one. The author's other inpaint node (VAE Encode For Inpaint V2) uses the same shrinking trick, with a comment explaining the point: a slightly-eroded mask keeps the generated pixels inside the face boundary, which makes the seam blend better and avoids eating the background around the face.
So: the name lies, but the behavior is probably what you want anyway. If you genuinely need to expand face masks, this is the wrong tool - reach for a plain mask-dilate node instead.
The constraint you'll trip on
The node requires mask.shape[0] (your mask's batch size) to exactly match the number of segments in the SEGS stream. If they don't match, it silently returns your original mask unchanged - no error, no warning, just a no-op. That silent-pass is the nasty one: you'll think you applied the per-face masks, and you won't notice nothing happened until the output comes out wrong. If you're getting mysteriously unprocessed masks, count your segs against your mask batch first.
Output is a single MASK (the processed per-face batch), which wires naturally into a per-face inpaint encode or a detailer.
Where it fits
Group-photo face fixes, one-face-at-a-time inpaint loops, or prep before VAE Encode For Inpaint V2 when you want the noise mask confined to each face individually. The workflow shape is: detector → mask → this node → inpaint encode per face → paste back.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Off-Live/ComfyUI-off-suite
# or: ComfyUI Manager → search "ComfyUI-off-suite"
No extra dependencies beyond ComfyUI's bundled OpenCV, but you do need Impact Pack for the SEGS type and a detector to produce it. Repo's been quiet since mid-2024 - frozen, but stable for this kind of utility.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — | |
| segs | SEGS | — | |
| dilation_factor | FLOAT | 1.00–10 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MASK | MASK | — |