Image Batch Filter (Swwan)
Throw out the black frames and get told which ones
- images
- replacement_image
- images
- removed_indices
Empty frames happen more than they should
A batch in ComfyUI is a tensor, and a tensor can't have holes. So when something upstream "skips" a frame - a loop that didn't run, a detector that found nothing, a segment that failed and got padded - you don't get a gap. You get a black frame, or a frame of some placeholder colour, sitting in the middle of your video like a missing tooth.
Image Batch Filter finds those frames and either removes them or replaces them. It's KJNodes' ImageBatchFilter re-registered by the Swwan pack, and it's the kind of node you don't know you need until you're scrubbing through a 120-frame render looking for the two frames that came out black.
How "empty" is decided
Each frame is compared against empty_color - a mean of the absolute per-pixel difference across the whole frame - and if that mean is at or below empty_threshold, the frame counts as empty.
empty_color- a string, default0, 0, 0. Integers only, on a 0–255 scale: each part is parsed and divided by 255.128, 0, 0is fine;0.0, 0.0, 0.0raises a conversion error, which surprises people who've just come from a node that wanted floats.empty_threshold- 0 to 1, default0.01. This is the tolerance, and it exists because a "black" frame that's been through a VAE encode/decode isn't really zero - it's a couple of levels off in every channel.0.01catches near-black without catching genuinely dark content. Lower it if you're dropping frames you wanted; raise it if a few are still sneaking through.
Two behaviours, one switch
Without replacement_image, empty frames are dropped and the batch comes back shorter. That's the mode you want when you're cleaning up a clip before encoding.
With replacement_image wired, empty frames are replaced in place with that image and the batch keeps its original length. The replacement is upscaled with Lanczos to match if the dimensions differ. One detail from the implementation: only the first frame of whatever you wire in is used, so a multi-frame replacement image gives you the same frame repeated.
Either way you get a second output, removed_indices - a STRING listing the indices that were caught, comma-separated. That's the good half of this node. It's trivially easy to log, easy to print, and easy to branch on: wire it into a text node and you can see which frames the filter considered empty, which is usually how you find the actual bug upstream.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/aining2022/ComfyUI_Swwan
cd ComfyUI_Swwan
python -m pip install -r requirements.txt
Manager → ComfyUI Swwan, restart, hard-refresh. No models, no optional dependencies; it's a mean-and-threshold over tensors.
Practical notes
It shortens the batch, and downstream nodes may not like it. Anything that assumed a fixed frame count - a model expecting 4n+1 frames, a conditioning tensor with a matching length - will now be mismatched. The replacement_image mode exists mostly for that: keep the count stable, swap the content.
Black isn't the only empty. If your failure mode is white frames, or a magenta placeholder, set empty_color to match. The node does no cleverness: it looks for the colour you tell it.
Frames that are legitimately dark will be caught. A fade-to-black in an otherwise good clip, or a night scene, can sit under the threshold. The removed_indices string is what saves you here - check the list before you trust a shortened output, because a filter that silently eats your opening fade looks exactly like a model that generated fewer frames than you asked for.
And note it only handles the colour test. Frames that are garbage but not empty - a smear, a duplicate - sail right through. For deduplication you want an index-node plus a comparison, not this.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| empty_color | STRING | 0, 0, 0 | — |
| empty_threshold | FLOAT | 0.010–1 | — |
| replacement_imageopt | IMAGE | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| removed_indices | STRING | — |