Image Mask Comparer
Check the logo survived, and re-roll the seed if it didn't
- image_a
- image_b
- mask
- is_match
- similarity
- image_out
You've just run an inpaint pass on a product shot, and the one thing you cannot afford to lose is the label. This node answers the question a human would otherwise have to eyeball - did the masked region survive? - and if the answer is no, it re-rolls the whole generation for you. Automatically. That's the part that separates it from every other "compare two images" node in the ecosystem: it doesn't just report, it acts.
It's a quality gate for automated or batch workflows, in the same spirit as the KB's product-photography recipe where preserving text and labels on products is the standing failure mode. Stick it at the end of an inpaint or composite graph, feed it the original, the result, and a mask over the critical region, and let it decide.
How the comparison works
No GPU, no model, no API key - it's pure numpy on the CPU. The node converts both images, uses the mask (anything above 0.5 counts as the region of interest), and scores only the pixels inside it. If image_b is a different size, it's resized to match first.
The score is a weighted blend of two metrics: 0.6 * (1 - mean absolute error) plus 0.4 * normalized cross-correlation. MAE catches how far apart pixels are on average; NCC catches whether the structure and brightness patterns line up. Neither is perceptual - a 0.95 here can still look wrong to a human eye - but it's a cheap, deterministic gate for "did this region come back close."
The inputs that matter
image_a- the reference (original).image_b- the generated result.mask- white where you care, black everywhere else.threshold(default 0.90) - anything at or above counts as a match. That default is stricter than it sounds; for slightly-blurred logos expect to back it off to ~0.85.max_retries(default 4) - how many attempts before it gives up.0turns the auto-retry off and makes it a plain comparator.
Outputs: is_match (a real BOOLEAN), similarity (the 0–1 score), and image_out, which passes image_b through so your graph can keep going after the check.
What the retry loop actually does
Here's the mechanism, and it's cleverer than it looks. On a mismatch the node deep-copies the currently-running prompt, randomizes the seed of every sampler it can find - KSampler, KSamplerAdvanced, SamplerCustom, RandomNoise, Impact Pack's KSamplerProvider/BasicScheduler, plus a generic scan of anything with a seed or noise_seed input - re-queues the whole thing (direct queue access, HTTP POST to /prompt as fallback), then calls interrupt_processing() so downstream nodes never see the bad result. It repeats until the check passes or retries run out.
Read that again: it re-queues your entire prompt and re-rolls every sampler seed, not just the inpaint pass. If your graph has a main KSampler feeding the inpaint, both get re-rolled. That's by design - it's a "regenerate until it works" loop - but it means you don't want this node babysitting a 20-minute Flux pass you're otherwise happy with.
When retries are exhausted, it stops interrupting and returns False, and downstream runs anyway. So design your graph so is_match=False is an explicit dead-end or alert, not an accident.
Installing it
Same pack as va1's pad node - clone once, get both. It depends on cv2 and numpy, which a stock ComfyUI already provides, so there's no separate requirements.txt dance.
cd ComfyUI/custom_nodes
git clone https://github.com/vaishnav-vn/va1.git
Restart, and Image Mask Comparer shows up under image/compare. ComfyUI Manager works too: Install from URL, paste the repo URL.
Gotchas
- An empty mask passes vacuously. If the mask is all black (or nothing is wired in), the node has zero masked pixels and returns
True, 1.0without complaint. A missing mask looks like a perfect result. This is the trap that will cost you - always sanity-check the mask. is_output_nodeis true, so it'll happily sit at the end of a graph as a terminal - butimage_outis there for when you want the pipeline to continue.- It relies on ComfyUI's internal
PromptServerandinterrupt_processinginternals. It works on a normal local install; if you're running ComfyUI behind a weird wrapper, the HTTP fallback covers some of that, and if both strategies fail the node prints a hint to enable Auto Queue for manual retries.
For its niche - "prove the label survived, or burn a few seeds until it does" - it's a genuinely useful automation piece. Just respect that re-roll button: it's full-graph by design.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image_a | IMAGE | — | |
| image_b | IMAGE | — | |
| mask | MASK | — | |
| threshold | FLOAT | 0.900–1 | — |
| max_retries | INT | 40–50 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| is_match | BOOLEAN | — |
| similarity | FLOAT | — |
| image_out | IMAGE | — |