Nodes/Orel's Nodes/Multi Reference Face Similarity
ComfyUI Node

Multi Reference Face Similarity

A QC metric, not a lie detector

By DarkOrel·Created 30 days ago·Updated 22 days ago· 1
Multi Reference Face Similarity
  • generated_image
  • reference_1
  • reference_2
  • reference_3
  • average_score
  • percentage
  • passed
  • verdict
  • score_ref1
  • score_ref2
  • score_ref3
◄modelbuffalo_l►
◄detection_size640►
◄threshold0.60►
◄use_reference_1true►
◄use_reference_2false►
◄use_reference_3false►

If you generate the same character a hundred times, at some point you want a number instead of vibes. This node gives you one: run InsightFace over your generated image and up to three reference images, compare the face embeddings, and tell you how close they landed.

The author is unusually careful about framing, and it's the right framing. From the node description: this is a workflow quality-control metric, not proof of real-world identity. Read that twice. Everything useful about this node, and every way it disappoints, follows from it.

The mechanism

Under the hood it's the standard face-recognition pipeline: detect, align, embed.

  1. Your IMAGE tensors are converted from ComfyUI's float 0–1 RGB batch format to 8-bit BGR, first batch item only.
  2. InsightFace's FaceAnalysis runs detection at the chosen resolution.
  3. The largest detected face is used - and only that one. A group photo doesn't get averaged; it gets whoever takes up the most pixels.
  4. Faces come back as normalised ArcFace embeddings, so comparison is a plain dot product = cosine similarity.
  5. Scores from the enabled references are averaged, and percentage is that average times 100.

That's it. Same math runs under IP-Adapter FaceID, InstantID, PuLID and ReActor - they're all front ends over this embedding step, which is why the community periodically notices how much of the stack rests on one library.

Inputs

  • generated_image (IMAGE, required) - the thing you're judging.
  • model - buffalo_l (default), buffalo_m, buffalo_s, buffalo_sc, antelopev2.
  • detection_size - 640 (default), 320, 160.
  • threshold (FLOAT, default 0.6, range -1 to 1).
  • use_reference_1/2/3 (BOOLEAN - ref 1 on, refs 2 and 3 off by default).
  • reference_1/2/3 (IMAGE, optional) - wire the reference faces here.

Bigger detection size finds smaller and more extreme-angle faces at the cost of speed. buffalo_l is the accurate general-purpose pack; buffalo_s/sc are the small, fast ones.

Outputs

  • average_score (FLOAT) - the mean cosine similarity across enabled references.
  • percentage (FLOAT) - the same times 100, ready for a filename or a log line.
  • passed (BOOLEAN) and verdict (STRING) - the threshold comparison, using the same default verdict strings as Face QC.
  • score_ref1/2/3 (FLOAT) - the individual scores; unused references report 0.0 so the sockets stay defined.

Those per-reference outputs are the interesting ones. If ref1 scores 0.72 and ref3 scores 0.41, your references disagree - probably one of them isn't the same face, or is too small, or is a three-quarter-profile shot the detector handled badly. The average hides that.

Installation, and the honest warning

The node needs requirements-face.txt, which is a different file from the standard one:

cd ComfyUI/custom_nodes
git clone https://github.com/DarkOrel/Orels-Nodes.git orels-nodes
python -m pip install -r orels-nodes/requirements.txt
python -m pip install -r orels-nodes/requirements-face.txt

That second file pulls insightface, onnxruntime, opencv-python-headless and numpy. Be aware of what you're signing up for: InsightFace has a long-standing reputation as one of the roughest installs in local generation, mostly because it historically wanted a C++ build toolchain and has a habit of dragging numpy backwards and breaking other packs that need numpy 2.x. The library finally shipped a PyPI release in May 2026 that dropped the build requirement, which helps - but if you're on an older environment and pip starts resolving numpy, stop and make a backup first.

Everything else in this pack loads fine without those packages. Only the face nodes fail, and they fail with a clear "InsightFace is not installed" message rather than a broken import at startup.

Licensing, since it matters here: the InsightFace library is MIT, but its pretrained ONNX models are non-commercial. If you plan to sell output from a pipeline that uses this node, that's the constraint - not the code.

Gotchas

No face means an error, not a zero. If the generated image or any enabled reference has no detectable face, the node raises. That's deliberate - the author's note says it refuses to invent a score - and it's the correct behavior, but it does mean a batch job will stop rather than quietly mark a bad image as failing.

A toggle with nothing wired raises too. Enable reference 2 without connecting reference_2 and you get a specific error naming the mismatch - a good error, easy fix. Same for enabling no references at all.

Thresholds don't transfer between setups. 0.6 is a reasonable starting point for same-person cosine similarity, but your model, references and lighting all shift the distribution. Score runs you're happy with, then set the line where your good ones live.

It's a proxy. A high score means two faces embed close together in one particular model's space. It doesn't mean the image looks like the person - same-face-different-person and same-person-different-pose both exist and fool this metric in opposite directions.

CategoryOrel's Nodes/Face

Inputs (10)

NameTypeDefaultDescription
generated_imageIMAGE—
modelCOMBObuffalo_l5 options: buffalo_l, buffalo_m, buffalo_s, buffalo_sc, antelopev2
detection_sizeCOMBO6403 options: 640, 320, 160
thresholdFLOAT0.60-1–1—
use_reference_1BOOLEANtrue—
use_reference_2BOOLEANfalse—
use_reference_3BOOLEANfalse—
reference_1optIMAGE—
reference_2optIMAGE—
reference_3optIMAGE—

Outputs (7)

NameTypeDescription
average_scoreFLOAT—
percentageFLOAT—
passedBOOLEAN—
verdictSTRING—
score_ref1FLOAT—
score_ref2FLOAT—
score_ref3FLOAT—