arkennemasis Verify Candidate (identity/frame/colour)
The QC step that used to live in the operator's head, now a node
- candidate
- plate
- candidate_mask
- plate_mask
- region_mask
- verdict_json
- report
- passed
- result
- colour_de
Before this node, quality control in a variation pipeline was a person squinting at a rendered image and deciding "is that still the same product, in the same frame, roughly the right colour?" - fine for a handful of shots, useless for 380, and impossible to hold consistent across runs. ArkVerifyCandidate turns that judgement into measurements: it compares a generated candidate against the locked base plate and emits a verdict - pass, soft, or hard - plus every number that went into it. This is the step that used to live in the operator's head, now automated and reproducible.
What it measures
Two required inputs: candidate (the generated image) and plate (the locked base photo). From there the checks are: product identity (is it still the same physical object), frame match (did the camera and composition hold), colour delta (ΔE2000 against the target hex - the standard perceptual colour metric), plus bleed and hygiene (is the intended region cleanly painted, nothing leaking into neighbouring regions).
The optional inputs are where the real verification power comes from:
plate_lock_json- the frozen measurements from Plate Lock. Without it only same-image checks are possible.recipe_json- supplies the calibrated tolerances.cell_json- supplies this cell's target region and target hex, "so nothing has to be typed per cell."target_region/target_hex- manual overrides. Blanktarget_hexskips the colour check, which the tooltip says is correct for a reference-image axis (no target colour exists).candidate_mask/plate_mask/region_mask- the crucial one isregion_mask: supplying the mask of the region this axis paints "turns the colour reading from a whole-frame average into a real measurement." Without it, the colour check measures everything; with it, it measures the thing you actually repainted.frame_tolerance,colour_tolerance_de,identity_tolerance- 0 means "take it from the recipe."enabled(default true) - flip off while tuning prompts to pass candidates through with no verdicts, accepting that nothing then stands between a drifted render and delivery.
Outputs: verdict_json (all measurements), report (human-readable), passed (BOOLEAN for gating), result (the pass / soft / hard string), and colour_de (the float colour delta, the one you'll wire into a threshold or a run log).
Why this design is right
The deliberate architecture here is separation of sources: identity and frame come from the plate lock, tolerances come from the recipe (which itself can be calibrated from a real labelled set via the pack's Calibrate node rather than guesswork), and the per-cell target comes from the cell definition. That means one node serves every cell of a run with no per-cell typing, and the same measurement code is what audits the whole batch. "Soft" verdicts are where an operator still gets a say; everything else is machine-checked and recorded.
Install
ArkVerifyCandidate is one of the 61 nodes in the comfyui-arkennemasis pack, in arkennemasis/Variation:
cd ComfyUI/custom_nodes
git clone https://github.com/Hishamahmer/comfyui-arkennemasis
pip install -r comfyui-arkennemasis/requirements.txt # then restart ComfyUI
Or ComfyUI Manager → Install via Git URL with the repo URL. It runs locally - no API, no key. It's pure image maths (identity/frame/colour measurements in the pipeline's colour module), which makes it the cheapest possible guard: verification costs nothing compared to the generation it's checking.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| candidate | IMAGE | The generated image. | |
| plate | IMAGE | The locked base plate. | |
| plate_lock_jsonopt | STRING | From Plate Lock — the frozen measurements to compare against. Without it only same-image checks are possible. | |
| recipe_jsonopt | STRING | Supplies the calibrated tolerances. | |
| cell_jsonopt | STRING | Supplies the target region and the target hex for this cell, so nothing has to be typed per cell. | |
| target_regionopt | STRING | Overrides the cell's region. | |
| target_hexopt | STRING | Overrides the cell's hex. Blank = skip the colour check, which is correct for a reference-image axis. | |
| candidate_maskopt | MASK | Optional explicit subject mask for the candidate. Omitted = derived from the image's own border colour. | |
| plate_maskopt | MASK | — | |
| region_maskopt | MASK | Optional mask of the region this axis paints. Supplying it is what turns the colour reading from a whole-frame average into a real measurement. | |
| frame_toleranceopt | FLOAT | 0.0000–1 | 0 = take it from the recipe. Non-zero overrides. |
| colour_tolerance_deopt | FLOAT | 0.00–100 | 0 = take it from the recipe. |
| identity_toleranceopt | FLOAT | 0.0000–1 | 0 = take it from the recipe. |
| enabledopt | BOOLEAN | true | Off: skip every measurement and pass the candidate through. Useful while you are still tuning prompts and want images without verdicts - but nothing then stands between a drifted render and delivery. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| verdict_json | STRING | — |
| report | STRING | — |
| passed | BOOLEAN | — |
| result | STRING | — |
| colour_de | FLOAT | — |