jz Image Sanity
The bouncer your batch workflows are missing
- image
- alpha
- image
- ok
- reason
- std
- mean
- report
- all_ok
jz Image Sanity is a quality gate for generated frames, and the more you lean on API image models or long batches, the more you'll want one. Fire off enough generations through Gemini or an upscaler running over a folder and you'll eventually get a frame back that is black, blown out to white, a single flat colour, or a fully transparent nothing. Most of the time you don't notice until you're staring at a finished grid. This node catches them at the moment they happen - and, crucially, it never raises. It measures each frame, reports what's wrong, and lets you decide whether that means retry, substitute, or skip.
That last bit is the whole design. This is from the comfyui-jz pack, which lives and dies by the pattern of branching on results with jz Switch and jz Fallback - lazy if/else nodes where the unused branch never executes. Image Sanity is the sensor those branches read. Wire its ok output into a jz Fallback alongside a retry path and you've built a loop that quietly discards garbage without a human in it.
How it works
Per frame, it runs a handful of cheap tensor checks. The interesting one is flatness, measured per channel - a flat red frame has per-channel standard deviations of 0, but a single global std across the whole tensor comes out around 0.47 because red vs. black is huge variation. So it computes std for each RGB channel and takes the largest; if that's below min_std (0.01 by default), the frame is flat. Brightness uses the BT.601 luma-weighted mean, so "too dark" and "too bright" are perceptual, not raw pixel averages. Transparency is separate: it looks at the 4th image channel and an optional wired alpha mask, and fails a frame whose alpha is zero everywhere - nothing visible, however textured the RGB is. An empty 0px frame is always a fail, no toggle.
The inputs that matter
check_flat/check_dark/check_bright/check_transparent- the toggles. All on by default.min_std,min_mean,max_mean- the thresholds. Defaults are tuned for "the API returned literal garbage," so they're tight: 0.98 for max_mean means a legitimately bright scene can trip it. If your workflow deliberately makes dark images, lowermin_mean(too dark means mean luma below it); if it runs bright and airy, raisemax_mean. Watch thestdandmeanoutputs while you tune.alpha+invert_alpha- the one real gotcha. LoadImage's MASK output is 1 = transparent, soinvert_alphadefaults to on to compensate. If you wire a mask from somewhere that's 1 = opaque, flip it.
Outputs, and how to wire them
The per-frame outputs - image, ok, reason, std, mean - are all lists, one verdict per frame in a batch. reason is a plain string ("flat #ff0000 (std 0.0000 < 0.0100)") that tells you exactly which check fired; report is a single JSON blob covering the whole batch and feeds jz Display JSON if you like reading the autopsy; all_ok is the one scalar you use for whole-batch decisions. The image output is a pass-through, untouched, so you can chain this inline without mangling anything.
Install
It ships inside comfyui-jz, so install the pack, not the node:
cd ComfyUI/custom_nodes
git clone https://github.com/j-zhang19/comfyui-jz
then restart ComfyUI, or use ComfyUI Manager and search "comfyui-jz". Dependencies are just requests, pillow, numpy - no model downloads, nothing heavy.
Gotchas
A wired alpha mask must match the frame dimensions or it raises (one mask broadcasts over a whole batch, fine). And remember the defaults are strict: this is a bouncer, not a critic. It's designed to flag anything a downstream model could choke on, so don't be offended when your artsy near-black frame gets thrown out - that's the feature.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| check_flat | BOOLEAN | true | fail frames with no variation — flat black, flat white, any single fill colour |
| min_std | FLOAT | 0.0100–1 | flat when the largest per-channel std falls below this; watch the std output to tune it |
| check_dark | BOOLEAN | true | fail near-black frames that carry just enough noise to pass the flat check |
| min_mean | FLOAT | 0.0200–1 | too dark when mean luma is below this |
| check_bright | BOOLEAN | true | fail blown-out near-white frames |
| max_mean | FLOAT | 0.9800–1 | too bright when mean luma is above this |
| check_transparent | BOOLEAN | true | fail frames whose alpha is zero everywhere — nothing visible, however textured the rgb is |
| alphaopt | MASK | transparency for a 3-channel image; LoadImage's MASK output goes here | |
| invert_alphaopt | BOOLEAN | true | LoadImage masks are 1 = transparent; keep on when wiring that, turn off if your mask is 1 = opaque |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| ok | BOOLEAN | — |
| reason | STRING | — |
| std | FLOAT | — |
| mean | FLOAT | — |
| report | STRING | — |
| all_ok | BOOLEAN | — |