Mask Statistics
The node that catches when your detector found nothing
- mask
- threshold
- index
- coverage
- covered_pixels
- total_pixels
- min
- max
- mean
- is_empty
- batch_size
- width
- height
- summary
Mask Statistics measures a mask instead of changing it - and the one measurement that matters is is_empty. Here's the scenario it exists for: a CLIPSeg prompt matched nothing, a SAM click missed, a threshold was set too high, and your pipeline just produced a mask of pure black. Nothing downstream says so. You inpaint a black mask, or crop to nothing, or save a file that shouldn't exist - and it all ran fine, which is somehow worse than an error. This node is the tripwire.
It's a WAS Node Suite mask node (WAS Suite/Image/Masking), and it's the rare mask node you'd call a control-flow tool: it doesn't make the mask, it tells you whether the mask is worth using.
How it works
One mask in, one threshold (default 0.5) deciding what "covered" means - a pixel must be above it to count. 0.5 is what the suite's other mask ops use; 0.1 counts faint feathering in; 1.0 counts nothing since no mask value exceeds it. The mask is read, never changed, so you can tap it off a wire on its way somewhere else without breaking the flow. scope measures the whole batch together or a single mask by index (negatives from the end, decimals truncated), with out_of_range controlling what an out-of-bounds index does.
The outputs are the story:
- coverage - fraction of pixels above threshold, 0.0–1.0. 0.25 = a quarter of the frame is masked.
- covered_pixels / total_pixels - the exact count and the denominator, for when area matters more than fraction.
- min / max / mean - the value range and average. These ignore the threshold. A
maxbelow the threshold means nothing was found, whatever the mask looks like in a preview; ameanwell belowcoveragetells you the edges are soft. - is_empty - true when no pixel clears the threshold. This is the one to wire into a gate.
- batch_size, width, height - plumbing: the number of masks on the wire and the mask's dimensions, so a loop can walk the batch or a crop/Empty Latent Image can follow the mask's size without you typing it twice.
- summary - every figure on one line as a STRING, for a log or burning into a frame with Image Draw Text.
Why you'd reach for it
The KB's detailing doc spells out the failure mode: detectors occasionally come back with nothing - a mask where a rock formation was mistaken for a face, or a crowd handled as twelve jobs. Automation only survives that if something checks the result. Wire is_empty into the suite's Any Gate or a switch, and the inpaint, crop, or save is skipped when a detector missed. Wire coverage into a Logic Compare Numbers against a minimum (say 0.05) and you reject a mask that technically found something but essentially didn't.
It's also the fastest way to sanity-check your own thresholding: if you can't tell whether a feathered mask is doing anything, coverage versus mean answers it numerically instead of by squinting.
Installing it
Standard WAS Node Suite v3:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui.git
or ComfyUI Manager → "WAS Node Suite v3", restart. ComfyUI 0.14.0+, Python 3.10+, no extra dependencies.
Where people get burned
The threshold is the trap. It's easy to leave it at the 0.5 default and then wonder why a very soft mask (all values under 0.5, e.g. after a strong blur) reports is_empty and gets skipped - that's the setting working as designed, so lower the threshold if faint coverage should count. Second, remember min/max/mean ignore the threshold: max is the real "is there anything here at all" test, because a mask full of values at 0.4 will have coverage 0 at threshold 0.5 but is clearly not empty. Check both before you trust either.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | The mask to measure, from CLIPSeg, SAM, a threshold or anything else. It is read, never changed, so it can be tapped off a wire on its way somewhere else. A batch is measured together unless index picks one mask of it. | |
| threshold | FLOAT,NUMBER,INT | 0.50–1 | Value a pixel must be above to count as covered. 0.5 = halfway, which the pack's other mask operations use; 0.1 counts faint feathering in; 1.0 counts nothing, since no mask value goes above it. min, max and mean ignore it. |
| scope | COMBO | whole batch | What to measure. `whole batch` answers one set of figures for every mask together; `one mask` measures the one the index picks. |
| index | INT,NUMBER,FLOAT | 0-16384–16384 | Which mask to measure, read only when scope is `one mask`. Counts from 0, and negatives count from the end: -1 = last, -2 the one before it. A decimal is truncated: 2.7 = 2. |
| out_of_range | COMBO | error | Index outside 0..batch_size-1, which index -1 never reaches. With 3 masks and index 4: `wrap` = mask 1, `clamp` = mask 2, `error` stops the prompt and names the batch size. |
Outputs (11)
| Name | Type | Description |
|---|---|---|
| coverage | FLOAT | Fraction of the pixels measured that are above the threshold, 0.0 to 1.0. 0.25 = a quarter of the frame is masked. Multiply by 100 for a percentage, or compare it against a minimum to reject a mask that found next to nothing. |
| covered_pixels | INT | How many pixels are above the threshold, counted exactly. The figure to test where an area in pixels matters more than a fraction of the frame, such as refusing a detection only a few hundred pixels across. |
| total_pixels | INT | Pixels measured: width times height for one mask, and that times batch_size at index -1. coverage is covered_pixels divided by this. |
| min | FLOAT | Smallest value measured, normally 0.0 to 1.0. Above 0.0 means no pixel is fully outside the mask, which a blur or a lifted floor causes and which makes a hard-edged paste bleed. |
| max | FLOAT | Largest value measured, normally 0.0 to 1.0. 1.0 means at least one pixel is fully inside. Below the threshold means nothing was found at all, whatever the mask looks like on a preview. |
| mean | FLOAT | Average of every value measured, before the threshold is applied. A feathered mask reads well below its coverage and a hard-edged one reads about the same, so the gap between the two says how soft the edges are. |
| is_empty | BOOLEAN | true when no pixel is above the threshold. Wire it into Any Gate or a switch so an inpaint, a crop or a save is skipped when a detector came back with nothing instead of running on a black mask. |
| batch_size | INT | How many masks arrived on the wire, whatever index was set. Wire it into a loop's iterations to walk the batch one mask at a time. |
| width | INT | Mask width in pixels. Feed it to a crop, a paste or an Empty Latent Image so the size follows the mask rather than being typed twice. |
| height | INT | Mask height in pixels. With width it gives the frame size a crop or an Empty Latent Image needs. |
| summary | STRING | Every figure on one line, as `index=all batch_size=1 512x512 threshold=0.500 coverage=25.00% covered=65536/262144 ...`. For a log, a console print, or burning into a frame with Image Draw Text. |