Person Selector Multi
1, make every mask
- current_image
- sam_model
- reference_1
- outfit_palettes
- person_mask
- depth_map
- sam3_model
- reference_2
- reference_3
- reference_4
- reference_5
- reference_6
- reference_7
- reference_8
- reference_9
- reference_10
- person_data
- face_masks
- head_masks
- body_masks
- combined_face
- combined_head
- combined_body
- aux_masks
- preview
- similarities
- matches
- matched_count
- face_count
- report
This is the node the whole pack orbits. Person Selector Multi detects every face in your image, matches each one against up to ten reference slots with exclusive 1:1 assignment - a face can match at most one reference, so no duplicate assignments - and generates the full mask set for every matched person: face, head, body, plus optional hair, skin, eyes, mouth, neck, and accessories, all from a single BiSeNet run. The output is PERSON_DATA, which is exactly the structure Person Detailer eats.
Why exclusive assignment matters: in a scene with three people who resemble each other, naive max-similarity matching happily assigns the same face to two references. This does a proper 1:1 assignment, so each character gets their own mask and their own detailing slot. If you've ever watched a multi-person detail pass "fix" the same face twice, you know why this is the feature.
How it works
The differentiator is appearance-enhanced matching. Pure ArcFace struggles when faces look similar, so the node blends four signals - face identity, hair color (BiSeNet HSV), head appearance (histogram), and outfit color - weighted by match_weights, default 50/15/15/20. Faces too similar? Crank the outfit weight. Need pure face matching? Set 100/0/0/0. That tuning string is the single most important thing to learn on this node.
The quality-of-life wiring is deep. auto_threshold (default on) finds the optimal assignment and ignores the manual threshold; guaranteed_refs force-assigns the first N references so key characters always get detailed. A depth map (from Depth Anything V2 or similar) enables edge-carved masks, cross-reference deconfliction, and back-to-front render ordering - the preview even draws an isometric depth-layer diagram. A person_mask (BiRefNet, RMBG-2.0) hard-clips every mask to the foreground silhouette. YOLO models dropped into models/ultralytics/segm/ add body-part detection, stored as aux masks.
The inputs that matter
current_image- supports batch input, each image processed independently.reference_1throughreference_10- all optional; with none connected, every face goes to the generic slot.match_weights- the four-signal blend.guaranteed_refs- force-assign your key characters.
Outputs
person_data (→ Person Detailer), per-reference face/head/body masks, combined masks, aux_masks, an annotated preview, plus similarities, matches, matched_count, face_count, and a report string.
Install
Face deps as usual:
cd ComfyUI/custom_nodes
git clone https://github.com/ping1979ping/comfyui-FVMtools
pip install insightface>=0.7.3 onnxruntime-gpu>=1.17.0 opencv-python>=4.8.0 numpy>=1.24.0
buffalo_l auto-downloads; parsing_bisenet.pth is manual; SAM/SAM3 and YOLO are optional.
Common issues
The wrong person gets the wrong slot → tune match_weights, or connect outfit_palettes from the color generator so the outfit signal has something to compare against. Everyone landing in the generic slot → no references connected, which is by design. Masks bleeding into the background → connect a person_mask or a segmentation detector. And if auto_threshold gives surprising results with strongly similar faces, the manual threshold path with guaranteed_refs gives you back control.
Inputs (36)
| Name | Type | Default | Description |
|---|---|---|---|
| current_image | IMAGE | Image(s) to search for faces. Supports batch input — each image is processed independently. | |
| auto_threshold | BOOLEAN | true | Auto: finds optimal 1:1 face-reference assignment (ignores threshold). Off: uses manual threshold. |
| threshold | FLOAT | 0.400–1 | Minimum cosine similarity to count as match. Ignored when auto_threshold is on. |
| guaranteed_refs | INT | 00–10 | Force-assign the first N references to their best matching face, ignoring threshold. 0 = off (default). 2 = Ref1 and Ref2 always get assigned. Useful when key characters must always be detailed. |
| aggregation | COMBO | How to combine similarity scores across multiple reference images of the same person | |
| mask_fill_holes | BOOLEAN | true | Fill holes inside the mask (closes gaps in segmentation) |
| mask_blur | INT | 00–100 | Gaussian blur radius for mask edges |
| det_size | COMBO | Face detection resolution — higher finds smaller faces but uses more VRAM | |
| aux_mask_type | COMBO | none | Additional mask type for aux_masks output (derived from BiSeNet, no extra cost) |
| aux_model | COMBO | YOLO model for body-part detection. Runs per batch image internally. Select a model from models/ultralytics/ to detect body parts (hands, persons, etc.). Detections are assigned to references via body mask overlap and stored as aux_masks in PERSON_DATA. none = no body-part detection (default) Drop .pt files into models/ultralytics/segm/ to add more models. | |
| aux_confidence | FLOAT | 0.350.05–1 | YOLO detection confidence threshold. Lower = more detections (may include false positives) Higher = fewer, more confident detections 0.25-0.35 recommended for most models. |
| aux_label | STRING | Filter YOLO detections by class label (substring match). Empty = keep all detected classes (default) Comma-separated: 'person' or 'leg,foot' Substring match: 'leg' hits 'Left-leg', 'right_leg', etc. The class list for the selected model is displayed above the Matching section after model selection. | |
| aux_fill_holes | BOOLEAN | false | Fill holes inside YOLO aux masks (closes interior gaps). Off by default — segm models usually produce solid masks. |
| aux_expand_pixels | INT | 00–100 | Dilate YOLO aux masks by N pixels (elliptical kernel). Useful when segm masks hug the silhouette too tightly for inpainting. 0 = no growth. |
| aux_blend_pixels | INT | 00–100 | Gaussian blur radius for YOLO aux mask edges (symmetric). Blurs ~N pixels inward AND outward from the current edge. If you want the grown shape to stay fully opaque, set aux_expand_pixels at least as large as aux_blend_pixels. |
| match_weights | STRING | 50/15/15/20 | Matching weight blend: face/hair/head/outfit. Controls how much each signal contributes to the final similarity score. 50/15/15/20 — balanced with outfit (default) 60/20/20/0 — no outfit matching 100/0/0/0 — pure face matching 40/15/15/30 — heavy outfit weight 3 values also work (outfit=0): 60/20/20 equals 60/20/20/0. Hair = BiSeNet hair color (HSV). Head = head crop histogram. Outfit = clothing region vs. palette color distribution. Values are auto-normalized, so 3/1/1/1 equals 50/17/17/17. |
| sam_modelopt | SAM_MODEL | SAM2 model from Impact Pack SAMLoader for body masks. Optional when sam3_model is connected. | |
| reference_1opt | IMAGE | Reference image(s) for person 1. Pass a batch of images of the same person for better matching accuracy. Optional — if no references connected, all detected faces go to the generic slot in PersonDetailer. | |
| outfit_palettesopt | IMAGE | Palette preview image batch for outfit color matching. Image[0] = palette for reference 1, image[1] = for reference 2, etc. If batch is smaller than number of references, remaining refs skip outfit matching. Connect palette_preview outputs from Color Palette Generator. Use match_weights with 4 values to control outfit weight (e.g. 50/15/15/20). | |
| person_maskopt | MASK | Foreground person mask (BiRefNet, RMBG-2.0, or similar). When connected, acts as a hard clip for ALL mask types — face, head, body, aux, and BiSeNet label seeds are zeroed outside this silhouette. Body masks are replaced entirely by the BiRefNet envelope (SAM/seed_grow skipped). Single mask [1,H,W] or per-image batch [B,H,W]. If batch size is 1 but current_image has a larger batch, the mask is broadcast to all images. Multi-person split: the foreground is divided per reference using depth (closest depth wins) when depth_map is also connected, or by face-center distance otherwise. When no references are connected, the split falls back to detected face count — each detected face becomes a pseudo-reference. | |
| depth_mapopt | IMAGE | Depth map batch from Depth Anything V2 or similar. Improves masks via edge carving and cross-reference deconfliction. | |
| depth_edge_thresholdopt | FLOAT | 0.050.01–0.3 | Depth gradient threshold for edge detection. Lower = more edges detected. |
| depth_carve_strengthopt | FLOAT | 0.800–1 | How strongly depth edges cut masks. 0=off, 1=full cut. |
| depth_grow_pixelsopt | INT | 300–200 | Gap filling between depth edges. 0 = no growing. |
| body_mask_modeopt | COMBO | auto | Body mask strategy: - auto: seed_grow (recommended) - seed_grow: BiSeNet + SAM seed, carved by image/depth edges - sam: legacy SAM-only body segmentation |
| depth_sort_orderopt | COMBO | front_last | Rendering order for PersonDetailer: - front_last: closest person rendered last (correct for depth_map with bright=near) - front_first: closest person rendered first (for inverted depth maps) - off: no sorting, uses slot order |
| sam3_modelopt | SAM3_MODEL_CONFIG | SAM3 model config (alternative to SAM2). Connect LoadSAM3Model output here to use SAM3 for body masks. When connected, SAM3 is used instead of SAM2 (sam_model). SAM3 supports text+point prompts and may produce tighter masks. | |
| reference_2opt | IMAGE | — | |
| reference_3opt | IMAGE | — | |
| reference_4opt | IMAGE | — | |
| reference_5opt | IMAGE | — | |
| reference_6opt | IMAGE | — | |
| reference_7opt | IMAGE | — | |
| reference_8opt | IMAGE | — | |
| reference_9opt | IMAGE | — | |
| reference_10opt | IMAGE | — |
Outputs (14)
| Name | Type | Description |
|---|---|---|
| person_data | PERSON_DATA | — |
| face_masks | MASK | — |
| head_masks | MASK | — |
| body_masks | MASK | — |
| combined_face | MASK | — |
| combined_head | MASK | — |
| combined_body | MASK | — |
| aux_masks | MASK | — |
| preview | IMAGE | — |
| similarities | STRING | — |
| matches | STRING | — |
| matched_count | INT | — |
| face_count | INT | — |
| report | STRING | — |