Face Similarity Score (ArcFace vs a reference folder)
Is every image in this folder actually the same person?
- candidate_image
- similarity
- passes_threshold
- output_folder
- score_label
The question it answers
"Are all of these the same person?" is the question nobody asks when scraping a reference folder, and it's the one that ruins datasets. One photo of the subject's sister, one photo of a lookalike, one photo where the face is too small for the recogniser to be sure - and now your LoRA blends two people, which is a training problem no caption can fix.
Face Similarity Score embeds the candidate against a folder of reference images using InsightFace ArcFace and gives you a number, a pass/fail and - this is the useful bit - the folder it thinks the image belongs in.
How it works
Both sides go through the pack's shared buffalo_l app (SCRFD detector plus a ResNet50 ArcFace recogniser), and only the largest face in each image is used. Embeddings are L2-normalised. Then one of three comparisons, which is the main decision you make:
mean_embedding- average every reference embedding into one centroid and compare once. Fast, standard, and what you want for a reference folder that's actually one person.average_scores- compare against every reference individually and average the N scores. More robust when a few of your reference images are outliers.any_match- keep if the candidate matches any single reference, and report the best-matching score. The most permissive of the three.
Reference embeddings are cached in memory, keyed by folder path, modified time and file count, so re-running 500 candidates doesn't re-embed the reference set every time. Detection failures get the pad-and-retry fallback the rest of the pack uses.
One thing to be clear about because the defaults mislead: keep_folder and review_folder are folder names, not save actions. The node doesn't copy anything. It returns the name of the folder the image belongs in, as a string, and you wire that into a save node (this pack's SaveImageWithName has a folder input for exactly this). If you were expecting files to appear, you're missing one connection.
Inputs, outputs, wiring
Required: reference_folder (absolute path; the default is the literal placeholder /path/to/reference/images), candidate_image, approach, threshold (0.45 to start), keep_folder (vix_keep) and review_folder (vix_review).
Outputs: similarity, passes_threshold, output_folder, score_label. The label is shaped sim_0.612_KEEP or sim_0.318_REVIEW and makes a decent filename suffix, or CSV column if you're logging. Two edge labels you'll meet: NO_REF_FACES (the reference folder didn't yield a single readable face - check the path and that the images are actually faces) and sim_0.000_NO_FACE (nothing detectable in the candidate). Both send the image to review_folder, which is the right call.
Install
ComfyUI Manager: search Portrait Dataset Tools. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/BillyBGit/ComfyUI-PortraitDatasetTools
pip install -r ComfyUI-PortraitDatasetTools/requirements.txt
insightface, opencv-python, numpy, Pillow, onnxruntime. First run downloads buffalo_l (~300 MB) to ~/.insightface/models/.
Where people get burned
0.45 is not the same scale as 0.9. This node compares one face to a folder centroid built from whatever photos you happened to include; Face Identity Guard compares two crops of the same shot. Different jobs, different numbers. The author's 0.45 starting point exists for this comparison, not for deciding whether a restoration preserved identity.
Your reference folder is the measurement. If it contains two people - a family album, a couple, a set you scraped from a search - the centroid is between two faces and every candidate scores mediocre. Before you trust the threshold, point the node's candidate_image at members of your own reference set and read the scores. That distribution tells you more than any default, and it's a far cheaper sanity check than discovering the problem on a 4-hour SUPIR batch.
Then the practical ones: CPU-only embedding is slow (it's ONNX on the CPU provider if the CUDA provider isn't there), so batch this on GPU if you have one. And the whole thing rests on the InsightFace models being non-commercial - the code you're reading is MIT, the weights aren't. For a personal LoRA that's irrelevant; for anything you sell, it isn't.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| reference_folder | STRING | /path/to/reference/images | — |
| candidate_image | IMAGE | — | |
| approach | COMBO | mean_embedding: average all reference embeddings into one centroid, single comparison (fast, standard). average_scores: compare to every reference individually and average the N scores (more robust to outlier reference images). any_match: KEEP if similarity >= threshold against ANY single reference (most permissive — best-match score is reported). | |
| threshold | FLOAT | 0.450–1 | Similarity ≥ this → KEEP. 0.45 is a good starting point. |
| keep_folder | STRING | vix_keep | — |
| review_folder | STRING | vix_review | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| similarity | FLOAT | — |
| passes_threshold | BOOLEAN | — |
| output_folder | STRING | — |
| score_label | STRING | — |