Segment Face
Getting a clean face mask with hair and neck toggles
- model
- image
- image
- mask
Segment Face is where the Character Face Swap pack goes from "we found a face" to "we have a precise face." A bounding box from Crop Face includes background, hair, and shoulders - useless for a clean paste. This node uses the BiSeNet face parser to classify every pixel as skin, brow, eye, nose, mouth, hair, neck, background, and so on, then produces two things: a mask of just the face region, and the image masked down to that region.
It's the workhorse of the pack's compositing half, and the output you'll care about most is the mask - that's what gets pasted back with Uncrop Face, and what Mask Contour traces for seam repair.
How it works
The pipeline is straightforward:
- The image is normalized with ImageNet mean/std and pushed through the BiSeNet model.
- Each pixel gets a class label (one of 19 categories). The code treats classes 1–13 as "face" - that's skin, left/right brows and eyes, eye glasses, ears, nose, mouth, upper and lower lip.
include_hair(defaultdisable) adds the hair class to the mask;include_neck(defaultdisable) adds the neck. Both off by default, because hair and neck are the two regions that most often cause paste artifacts - they have irregular edges and strong lighting variance.expand(default 0) runs a morphological dilation on the resulting mask by that many pixels, padding the region slightly so the eventual paste doesn't shave off the face edge.
The node returns both the mask and the masked image (the face-only pixels, background zeroed out), which is handy for eyeballing exactly what the parser thinks is face before you commit to it.
The settings that matter
- expand - 0 keeps the mask tight; a small value (4–8) gives the mask breathing room and helps the seam blend later. Too much and you drag in background.
- include_hair / include_neck - default off is usually right. Turn hair on only if the character's hairstyle is essential to the swap (it will make the mask much harder to blend cleanly); turn neck on when the crop includes a visible neck you don't want to chop.
Wiring the outputs
- image → the masked face image. Feed to the swap's paste pipeline or a preview to sanity-check.
- mask → into Uncrop Face (to control where the new face blends) and Mask Contour (to find the seam for inpainting).
Installing the pack
ComfyUI Manager → search Character Face Swap → install → restart, or:
cd ComfyUI/custom_nodes/
git clone https://github.com/ArtBot2023/CharacterFaceSwap.git
cd CharacterFaceSwap
python install.py # on the bundled Windows build: ../../../python_embeded/python install.py
The install.py step installs the bundled facexlib (BiSeNet lives there). Weights download on first load into ComfyUI/models/facexlib/. It also needs torchvision (already present in ComfyUI).
Common problems
- CUDA error at this node: BiSeNet inference is hardcoded to
.cuda()in the pack's source - no CPU path. If you're not on an NVIDIA/CUDA setup, this is where the pipeline stops. - Mask includes hair you didn't ask for: double-check
include_hairisdisable; BiSeNet is usually right but around a messy hairline it can label hair as skin. Bumpexpanddown to 0 if the mask is leaking. - Holes in the mask: dark sunglasses or heavy shadow can get classified as eye-glasses or background. That's the parser being honest - either accept it or feather with GrowMask.
If your masks look right, the rest of the swap is downhill. If they don't, fix the mask before you paste anything - garbage in, garbage (and visibly jagged edges) out.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| model | BISENET | — | |
| image | IMAGE | — | |
| expand | INT | — | |
| include_hair | COMBO | disable | 2 options: enable, disable |
| include_neck | COMBO | disable | 2 options: enable, disable |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |