ComfyUI Node

Identity QA

A number for 'has my character changed colour by keyframe 12'

By meryboth·Created 25 days ago·Updated 23 days ago· 1
Identity QA
  • view_a
  • view_b
  • view_c
  • layout
  • report
  • passed
◄ceiling0.030►

The whole reason this pipeline exists is that independent generations of the same character drift. So the last node in the graph asks the obvious question: did it? It prints a PASS or a FAIL, a drift number per view, and a boolean you can wire into whatever you want.

It is not a face-ID check. Nothing here embeds anything - you're looking at a clay mascot, not a person. It measures how far the character's dominant colours moved between the first keyframe and the rest. That sounds crude, and it is deliberately so: the obvious smarter metric had to be thrown out, and knowing why is most of what's interesting here.

How it works

Anchor, per view. Each view's k00 (the rest pose) is reduced to six dominant colours by k-means over its subject pixels - everything that isn't the flat background. It's the view's own anchor, not a shared one, and that matters: k00 came out of the same video encoder as every other frame, so anything it shares with them is codec, not drift. The k-means is seeded, because a QA number that moves between runs of identical data isn't a QA number.

Drift, per panel. Each panel's subject pixels are assigned to the nearest anchor colour, and the drift is how far each colour's mean has moved, weighted by how much of that colour the anchor contained - not how much the panel contains. That weighting is the trick. A raised arm exposes more body and less backpack, which shifts a colour histogram hard without the character having changed at all. Measured on a clip whose identity visibly holds, the naive histogram distance moved by 0.298 on the back panel from pose alone - bigger than any real drift in the run, and pointing at the wrong panel. Weighting from the anchor side drops that pose leak to 0.006–0.016, while a barely visible 8/255 red tint still reads three times baseline.

Colours the pose hid are skipped, not measured from a few dozen edge pixels.

Then it aggregates and gates: mean and max drift, per view, against ceiling.

Calibrating the ceiling

The tooltip is the author's own and it's the best guide you'll get:

  • 0.01 is codec noise.
  • 0.03 is a tint you'd notice if you looked for it - the default.
  • 0.07 is a character that has changed colour.

So a gate that passes at 0.03 is not saying "identical". It's saying "not worth re-running".

Inputs and outputs

view_a, view_b, view_c - the batches from Triptych Split. layout - needed for two things: the background colour, to build the subject mask, and the panel order for the per-view lines. ceiling - 0.03 default, 0.005 to 0.2.

report (STRING) - PASS/FAIL, then a line per view (front mean 0.0109 max 0.0209), then the verdict's notes. It also shows in the node's own UI text, so you don't need to wire anything to read it.

passed (BOOLEAN) - the hook for automation: drive a downstream switch, a mute group, or a save/panic branch. It's an output node, so it always runs.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/meryboth/from-mesh-to-motion
# restart ComfyUI

Or ComfyUI Manager → from-mesh-to-motion, or comfy node install from-mesh-to-motion. Pillow and numpy; the whole measurement is CPU-only numpy over 48 small crops. There's also python -m pipeline qa from the cloned repo, the same measurement outside the graph.

Where people get burned

PASS does not mean the sheet is good. The gate is pose-blind by construction - that's what makes it useful - so it tells you nothing about whether the character kept its shape. The pack's own third test character, a wire-thin robot with a loose scarf, drifted to 0.0522 max and got flagged as outside scope: thin profiles and free-moving cloth are the documented failure modes. Look at the side column yourself.

A too-small anchor raises. If k00 is nearly all background, or the background colour the node is comparing against doesn't match the actual canvas (you changed background in Triptych Compose and the clip came back on a different grey), the subject mask eats the character and you get "Anchor has too few subject pixels to build a palette." That's a configuration error, not a drift result.

Don't reach for a repair pass before you've read the number. The repo ships the repair machinery (pipeline/repair.py) with a guard that matters: an edit model asked to restore a character can restore it into a different pose, which makes the panel match the anchor beautifully and makes it worthless, because pose is the only thing this sheet is authoritative about. That's caught by a silhouette IoU floor of 0.90, and only there. The node itself doesn't repair; it tells you whether repairing is worth attempting.

Categoryfrom-mesh-to-motion

Inputs (5)

NameTypeDefaultDescription
view_aIMAGE—
view_bIMAGE—
view_cIMAGE—
layoutFMM_LAYOUT—
ceilingFLOAT0.0300.005–0.2Drift above this fails the sheet. 0.01 is codec noise, 0.03 a tint you would notice, 0.07 a character that has changed colour.

Outputs (2)

NameTypeDescription
reportSTRING—
passedBOOLEAN—