jz Double Threshold
A trimap from two sliders
- image
- input_alpha
- image
- trimap
- alpha
A regular threshold turns an image into two buckets: bright and dark. jz Double Threshold gives you three, which is the whole trick: luma at or above high goes pure white, luma at or below low goes pure black, and the band in between goes transparent. That transparent middle is the "unknown" zone - the thing that makes this a trimap builder rather than just another binarizer.
Where does that actually matter? Matting and compositing. If you're keying out a background or building a mask for a composite, a hard two-way split leaves ragged edges wherever the background and subject blur together; the soft middle band is where a proper blend happens instead. The node even names its intended downstream in the source: the RGBA output feeds jz Composite, which blends a 4-channel source through its alpha automatically. So the pair is "make a trimap, then paste it softly."
The inputs that matter
- low and high - two FLOAT sliders (0–1), defaults 0.15 and 0.85. Luma is BT.601-weighted (0.299/0.587/0.114), same math as the pack's jz Seam Carve.
lowmust be belowhigh, or the node refuses with a clear error. - input_alpha (optional) - the MASK output from a Load Image node. The reason: a PNG's transparency gets stripped into Load Image's MASK output, and without wiring it here, transparent pixels get thresholded on their (often black) RGB values and come out looking wrong. Feed the mask in and transparent input stays transparent.
- invert_input_alpha - default
true, which matches Load Image's convention where mask=1 means transparent. If your mask is 1 = opaque, turn it off.
The three outputs
- image - RGBA: white where bright, black where dark, transparent in the middle band. This is the one to wire into jz Composite.
- trimap - a MASK with 1 = white, 0 = black, 0.5 = the unknown band. Classic trimap for matting; also handy if you want to see the three zones.
- alpha - a MASK with 1 where a pixel was decided (white or black), 0 in the transparent band. Use it to feed a blur or feather, or to know where the node actually committed.
Installing it
It's in the comfyui-jz pack:
cd ComfyUI/custom_nodes
git clone https://github.com/j-zhang19/comfyui-jz
Restart ComfyUI (or Manager → "comfyui-jz"), find it under jz/image. No models, no GPU; deps are just requests, pillow, numpy.
Common issues
The classic beginner stumble is the transparent-LoadImage trap: you load a PNG with alpha, skip input_alpha, and the transparent parts come out black because their RGB is black. Wire the MASK output from Load Image into input_alpha and keep invert_input_alpha on - that's the intended setup, spelled out in the tooltips. Second: low ≥ high errors immediately; the sliders don't cross-check each other, so if you drag past, the node tells you. And the alpha input has to match the image's dimensions - Load Image's constant 64×64 placeholder mask is detected and treated as "nothing wired," so that particular false alarm is handled for you.
Usual personal-pack note: solo author, zero community footprint, README as documentation. The node itself is well-behaved enough that you'll learn it from the tooltips.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| low | FLOAT | 0.150–1 | luma at or below this -> black |
| high | FLOAT | 0.850–1 | luma at or above this -> white |
| input_alphaopt | MASK | input transparency; LoadImage's MASK output goes here | |
| invert_input_alphaopt | BOOLEAN | true | LoadImage masks are 1 = transparent; keep on when wiring that, turn off if your mask is 1 = opaque |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| trimap | MASK | — |
| alpha | MASK | — |