Mask Contour
The node that draws the seam you'll inpaint
- mask
- MASK
In a face swap there's a moment where the pasted face is in place but you can still see the join - a faint line where the swapped region meets the original skin. That's the seam, and it's the reason inpainting exists in this pack. Mask Contour is the node that finds the seam: it takes a face mask, traces the outline of it, and hands you back a thin mask of just the boundary.
It's a utility node, but it's the one that makes the pack's "seam fix" step work. Feed it the face mask from Segment Face, and it returns a hairline outline of that mask's outer edge - exactly the region you'd regenerate to melt the swap into the surrounding skin.
How it works
The mechanism is textbook OpenCV: it converts the mask to a uint8 array, runs cv2.findContours with RETR_EXTERNAL (only the outer boundary, ignoring holes) and CHAIN_APPROX_SIMPLE, then draws those contours back into a blank mask with 1-pixel-wide strokes. Output: a MASK that is 1.0 only on the boundary, 0.0 everywhere else.
In the shipped workflow that thin contour gets a GrowMask applied to thicken it into an actual inpaint region - a few pixels of seam is too fiddly to regenerate as-is, and growing it gives the inpaint model room to blend. So think of Mask Contour as "find the seam," and whatever comes after as "give the seam some thickness."
Wiring it
- mask (MASK) → the face mask whose boundary you want. From Segment Face, or any mask of the swapped region.
- Output (MASK) → into GrowMask (or straight into a mask-based inpaint setup) for seam repair.
Installing the pack
It's part of Character Face Swap: 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
OpenCV is already a ComfyUI dependency, so this node adds nothing heavy - no models, no downloads. Run install.py for the pack's facexlib dependency regardless.
Common problems
- Empty output mask (all black): your input mask is all-zeros or already a contour. Make sure you're feeding a filled region mask from Segment Face, not a thin outline.
- Contour too thin to matter: that's expected - this node outputs 1px by design. Thicken with GrowMask before inpainting, as the workflow does.
- Ragged, noisy outline: if the source mask has noisy edges (common with hair included in the segment), the contour inherits that. A bit of GrowMask blur/smoothing downstream cleans it up.
Quiet, small, and easy to overlook - but it's the node that turns "a pasted face" into "a face whose seam you can actually fix."
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MASK | MASK | — |