Nodes/Portrait Dataset Tools/Face Identity Guard (revert a pass that drifted the face)
ComfyUI Node

Face Identity Guard (revert a pass that drifted the face)

The undo button for a restoration pass that swapped your subject

By BillyBGit·Created 23 days ago·Updated 22 days ago· 1
Face Identity Guard (revert a pass that drifted the face)
  • image
  • reference
  • image
  • passed
  • similarity
  • suffix
  • report
◄min_similarity0.90►
◄suffix_if_reverted_noid►
◄suffix_in►

The failure it exists to catch

Generative upscalers don't preserve identity, they build a plausible one. SUPIR is the famous case: it captions the image with LLaVA and restores toward what that caption describes, so a face it doesn't know gets rebuilt into a face that fits. The breakout praise thread for SUPIR had the identity objection as its top reply, which tells you everything about how quickly people noticed.

For a one-off restoration you notice a swapped face and fix it. For a batch of 500 photos feeding a LoRA dataset, you don't - and then the model learns the wrong face. Face Identity Guard is the automated version of "did this pass keep the same person", and it doesn't need a curator awake.

How it works

It embeds the largest face in the candidate image and the largest face in the reference image with InsightFace ArcFace (buffalo_l), normalises both, and takes the cosine similarity. Below min_similarity it returns the reference - the pre-pass image - instead of the candidate. That's it. No confidence-weighted blending, no partial revert, no cleverness.

The conservative direction is deliberate and worth internalising: if the face can't be read in either image, the node returns the reference. No measurement, no substitution. Missing faces never get silently treated as passes.

The author's measured reference point is that a good SUPIR pass lands around 0.94 on a tuned crop, with >0.9 being comfortably the same person - hence the 0.90 default. That number comes from one tuned setup, so do what you'd do with any ArcFace threshold: run ten known-good pairs through it first and look at the similarity output before you trust a batch. ArcFace cosine moves with crop, expression and lighting even for the same person, and the numbers in the identity-preservation literature (PuLID 0.733 vs InstantID 0.725 on DivID-120) are on a different job entirely - don't port them.

Inputs, outputs, wiring

image is the candidate (the SUPIR result), reference is what it came from (the crop you passed in). min_similarity defaults to 0.90. suffix_if_reverted defaults to _noid, and there's an optional suffix_in for carrying tags from upstream so a reverted image keeps whatever _rev or _col it already had.

Outputs: image (either the candidate or the reference), passed (boolean), similarity (float), suffix, and report (a one-line human-readable summary). Wire image into your save node, suffix into that same save node's suffix input so _noid ends up in the filename, and report/similarity into whatever you use for logging. passed is what you count if you want to know your pass rate, and it's the number to watch: if most of a run comes back reverted, your restoration settings are too aggressive, not your guard.

Install

ComfyUI Manager: search Portrait Dataset Tools and install, then restart. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/BillyBGit/ComfyUI-PortraitDatasetTools
pip install -r ComfyUI-PortraitDatasetTools/requirements.txt

Dependencies are insightface, opencv-python, numpy, Pillow, onnxruntime. First run downloads buffalo_l (~300 MB) into ~/.insightface/models/.

Where people get burned

The guard costs you two face-detection passes per image on top of your restoration pass. On CUDA that's noise; on CPU-only onnxruntime it's the slowest part of the workflow, and it's the usual reason someone concludes the pack "makes everything crawl".

Tight, edge-to-edge crops are the other one, but it's handled: SCRFD's anchors expect a face inside a scene, so both this node and the rest of the pack pad-and-retry when detection fails. What isn't handled is a chain that drifted systematically - the guard will faithfully hand you back the original for every image, and what you actually need is to fix the pass. If your SUPIR branch is running at high denoise, that's the culprit; the pack's example workflow sits at 0.65 and notes that 0.75+ collapses.

And the licence note applies to anything built on this: the code is MIT, the InsightFace models you're invoking are non-commercial. Personal dataset work, fine. Selling the pipeline, read the terms first.

Categoryimage/analysis

Inputs (5)

NameTypeDefaultDescription
imageIMAGEThe candidate (e.g. the SUPIR result).
referenceIMAGEWhat it came from (e.g. the crop before SUPIR).
min_similarityFLOAT0.900–1Below this, fall back to the reference. ArcFace cosine: >0.9 is comfortably the same person; a good SUPIR pass measures ~0.94.
suffix_if_revertedSTRING_noid—
suffix_inoptSTRING—

Outputs (5)

NameTypeDescription
imageIMAGE—
passedBOOLEAN—
similarityFLOAT—
suffixSTRING—
reportSTRING—