Nodes/WAS Node Suite v3/Mask Statistics
ComfyUI Node Runs on cloud

Mask Statistics

The node that catches when your detector found nothing

By WASasquatch·Created 3 years ago·Updated 4 days ago· 1,844
Mask Statistics
  • mask
  • threshold
  • index
  • coverage
  • covered_pixels
  • total_pixels
  • min
  • max
  • mean
  • is_empty
  • batch_size
  • width
  • height
  • summary
scopewhole batch
out_of_rangeerror

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 max below the threshold means nothing was found, whatever the mask looks like in a preview; a mean well below coverage tells 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.

CategoryWAS Suite/Image/Masking

Inputs (5)

NameTypeDefaultDescription
maskMASKThe 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.
thresholdFLOAT,NUMBER,INT0.50–1Value 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.
scopeCOMBOwhole batchWhat to measure. `whole batch` answers one set of figures for every mask together; `one mask` measures the one the index picks.
indexINT,NUMBER,FLOAT0-16384–16384Which 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_rangeCOMBOerrorIndex 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)

NameTypeDescription
coverageFLOATFraction 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_pixelsINTHow 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_pixelsINTPixels measured: width times height for one mask, and that times batch_size at index -1. coverage is covered_pixels divided by this.
minFLOATSmallest 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.
maxFLOATLargest 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.
meanFLOATAverage 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_emptyBOOLEANtrue 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_sizeINTHow 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.
widthINTMask 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.
heightINTMask height in pixels. With width it gives the frame size a crop or an Empty Latent Image needs.
summarySTRINGEvery 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.