Nodes/Artfat Face Consistency/Artfat Face Consistency (Batch)
ComfyUI Node

Artfat Face Consistency (Batch)

Turn a folder of generations into a scored contact sheet in one run

By artfat-creator·Created about a month ago·Updated 18 days ago· 4
Artfat Face Consistency (Batch)
  • reference_image
  • contact_sheet
  • csv_path
  • mean_score
  • report
input_folder
keep_threshold0.36
reject_threshold0.28
reference_folder
sort_copiesfalse
contact_cols6
adaptive_thresholdfalse
keep_large0.60
keep_small0.50
reject_large0.40
reject_small0.30
frac_large0.15
frac_small0.03
detectorYuNet (fast, CPU)
yolo_model
detect_conf0.50
crop_padding0.30
min_face_frac0.00

The sibling node to Artfat Face Consistency (Sort) is for the other half of the job. Sort gates frames live, mid-generation. Batch takes the pile of images you already generated - a few hundred PNGs of your persona in every pose you could think of - and turns it into a labelled contact sheet, a CSV of scores, and optionally sorted keep/reject copies. If your character-consistency workflow ends with you manually culling renders, this is the node that retires that chore.

It runs the exact same scoring core as Sort: OpenCV YuNet detects the face, SFace embeds it, and cosine similarity against a reference says how much it looks like your person. Pure cv2 + numpy on CPU - no VRAM, no API, no key. One neat trick: give it no reference at all and it auto-selects the medoid of your input set as the anchor - the most central image - so even a folder of a single character's renders produces a useful ranking. Better still to bring a real reference, because judging a dataset against its own middle is a weaker bar than judging it against who it's supposed to be.

The inputs that matter

  • input_folder - the folder of images to analyse. That's the whole job in one field.
  • keep_threshold - cosine at/above this means keep. The default 0.363 is SFace's own same-identity baseline.
  • reject_threshold - below this means reject; anything between the two is borderline.
  • reference_image / reference_folder - 1..N reference shots, merged into a centroid.
  • sort_copies - also copy images into keep / borderline / reject / _noface subfolders, if you want the actual culling done on disk rather than just scored.
  • contact_cols - columns on the contact sheet (1–12).

What comes out

contact_sheet is an IMAGE you can wire into a Preview - every thumbnail stamped with its verdict and score, so you can scroll a few hundred faces in one glance. There's also csv_path (where the CSV was written), mean_score, and a report string. The CSV logs each image's face_frac and the threshold that was actually applied, which is the part you'll care about later.

The same size-aware thinking from Sort shows up here, scaled to both bars: flip adaptive_threshold on and close-ups get judged strictly (keep_large / reject_large) while small, far faces get the lenient end (keep_small / reject_small), with frac_large / frac_small defining the bands. A full-body shot of the right person stops being failed for having a small face. It's off by default.

Install

Same as the Sort node - one pack, two nodes. ComfyUI Manager (search "Artfat Face Consistency"), or:

cd ComfyUI/custom_nodes
git clone https://github.com/artfat-creator/ComfyUI-Artfat-FaceConsistency

Restart ComfyUI; they land under artfat. Dependencies are opencv-python + numpy (both ship with ComfyUI) and the detector models are bundled, so there's no download step. The optional YOLO detector needs ultralytics and a face .pt in models/ultralytics/bbox/.

Troubleshooting

  • "No images in '…'" - the folder path is wrong or empty; the node raises rather than silently doing nothing.
  • "No faces found anywhere" - no detector found a face in the whole folder, so there's nothing to anchor to. If your images genuinely have faces, that's a detector issue - try the YOLO option.
  • Near-zero scores skewing things - the mean_score drops embeddings below ~0.1, because those are SFace failing to embed a crop (a detector glitch), not real identity mismatches. Don't set thresholds against them.
  • Weird paths on Windows - the code decodes images via numpy instead of cv2.imread, so non-ASCII and spaced paths work where stock OpenCV chokes. A real, if niche, pain point handled for you.

The genuinely useful move is to run Batch once with a csv_path, read where your persona's real frames land per face size, and carry those numbers into the Sort node for live gating. Batch is the audit, Sort is the bouncer - they share one scoring core, so numbers transfer cleanly. And like its sibling, it's fully MIT with no InsightFace models in the mix, which puts it well ahead of most face tooling on licensing.

Categoryartfat

Inputs (19)

NameTypeDefaultDescription
input_folderSTRINGFolder of images to analyse.
keep_thresholdFLOAT0.36-1–1Cosine >= this -> keep (SFace same-id = 0.363).
reject_thresholdFLOAT0.28-1–1Cosine < this -> reject; between = borderline.
reference_imageoptIMAGEReference identity 1..N (batch). Empty = auto-medoid of the input set.
reference_folderoptSTRINGOptional folder of references, merged with reference_image.
sort_copiesoptBOOLEANfalseAlso copy images into keep/borderline/reject/_noface subfolders.
contact_colsoptINT61–12
adaptive_thresholdoptBOOLEANfalseON = keep/reject thresholds scale with face size (close-ups strict, small/far faces lenient). OFF = flat keep_threshold/reject_threshold.
keep_largeoptFLOAT0.60-1–1Adaptive: keep threshold for LARGE/close faces (face_frac >= frac_large).
keep_smalloptFLOAT0.50-1–1Adaptive: keep threshold for SMALL/far faces (face_frac <= frac_small).
reject_largeoptFLOAT0.40-1–1Adaptive: reject threshold for LARGE/close faces.
reject_smalloptFLOAT0.30-1–1Adaptive: reject threshold for SMALL/far faces.
frac_largeoptFLOAT0.150–1Adaptive: face-area fraction at/above which the LARGE thresholds apply.
frac_smalloptFLOAT0.030–1Adaptive: face-area fraction at/below which the SMALL thresholds apply.
detectoroptCOMBOYuNet (fast, CPU)YuNet = fast cv2, great on portraits. YOLO = finds small faces in full-body/complex shots, then crops+re-aligns so the score is fair.
yolo_modeloptCOMBOFace .pt model used when detector = YOLO.
detect_confoptFLOAT0.500.05–0.95YOLO confidence. LOWER = catches harder/smaller/angled faces.
crop_paddingoptFLOAT0.300–1Expand the detected face box by this fraction before scoring (more context).
min_face_fracoptFLOAT0.000–0.5Ignore faces smaller than this fraction of the frame (0 = off). Skips background faces.

Outputs (4)

NameTypeDescription
contact_sheetIMAGE
csv_pathSTRING
mean_scoreFLOAT
reportSTRING