ComfyUI Node

Mask Tools (C2C)

Mask Tools (C2C)

By Code2Collapse·Created 8 months ago·Updated about 18 hours ago· 60
Mask Tools (C2C)
  • image
  • mask
  • mask_b
  • plate
  • size_as
  • mask
  • image
  • report
◄modekey: colour►
◄colour_key_color#FFFFFF►
◄colour_key_colorspacergb►
◄colour_key_tolerance50►
◄colour_key_soft_falloff10►
◄colour_key_invertfalse►
◄luma_key_modeauto►
◄luma_key_low0.00►
◄luma_key_high1.00►
◄luma_key_gamma1.00►
◄luma_key_falloff1.0►
◄luma_key_invertfalse►
◄luma_key_channelluma►
◄luma_key_low_soft0.00►
◄luma_key_high_soft0.00►
◄luma_key_invert_keyfalse►
◄difference_key_tolerance0.080►
◄difference_key_softness0.050►
◄difference_key_invertfalse►
◄grade_gain1.00►
◄grade_gamma1.00►
◄grade_clamptrue►
◄grade_invertfalse►
◄grow_left0►
◄grow_right0►
◄grow_up0►
◄grow_down0►
◄grow_feather0►
◄combine_opunion►
◄gradient_width512►
◄gradient_height512►
◄gradient_typelinear►
◄gradient_angle0.0►
◄gradient_center_x0.50►
◄gradient_center_y0.50►
◄gradient_start0.00►
◄gradient_end1.00►
◄grain_amount6►
◄grain_seed0►
◄grain_size4►
◄grain_invertfalse►
◄motion_blur_angle0.0►
◄motion_blur_distance20►
◄motion_blur_invertfalse►
◄edge_spread4►
◄edge_spread_invert_maskfalse►
◄shuffle_out_RR►
◄shuffle_out_GG►
◄shuffle_out_BB►
◄shuffle_out_AA►
◄shuffle_premultiply_outputfalse►
◄transform_expand_x0►
◄transform_expand_y0►
◄transform_blur_x0.0►
◄transform_blur_y0.0►
◄transform_offset_x0►
◄transform_offset_y0►
◄transform_feather0.0►
◄transform_threshold0.50►
◄transform_invertfalse►

Half the nodes in a mature ComfyUI graph exist because someone needed one mask operation and chained four nodes to get it: a luma key, then a shrink, then a grain pass because the matte flickers. Mask Tools (C2C) puts eleven of those jobs behind one mode dropdown, and shows you the controls for the one you picked.

What it is

Keyers and matte operations in one node: colour, luma and difference keys; a grade pass; per-side grow/shrink; combine; gradient; grain; motion blur; edge spread; channel shuffle. It's the node you reach for when the matte is almost right.

Plenty of it is a torch-native port of chflame163's LayerStyle LayerMask family - mask_by_color, create_gradient_mask, mask_gradient, mask_grain, mask_motion_blur, pixel_spread - plus the pack's own luminance keyer, which used to be a separate "Luminance Keyer" node. Saved workflows pointing at the old node migrate automatically.

Why you'd reach for it

The wider KB is blunt about this layer: post-processing.md argues these deterministic operations are what you reach for instead of burning a diffusion pass on a job a lookup table does perfectly. Detection gets all the attention - masking-detection-detailing.md quotes the community line that "you can get that mask in SO many ways" - but the matte SAM or BiRefNet hands you still needs cleanup, and core ComfyUI offers ImageToMask and a threshold.

The modes, and what's real about each

key: colour - pick #RRGGBB, set tolerance, choose your distance metric. rgb treats dark navy and dark brown as similar because they sit at near-identical luminance; lab separates them. colour_key_soft_falloff is a graded band outside the tolerance, and it exists to stop a colour key "cutting like a pair of scissors".

key: luma - BT.709 luminance (0.2126R + 0.7152G + 0.0722B), with a channel selector that also does red/green/blue, hue, saturation, value and LAB L/a/b - so you can pull a green spill off a matte by keying the a channel instead of chaining three nodes. Presets auto, highlights (0.7–1.0), midtones (0.3–0.7), shadows (0.0–0.3) and custom. Here's the trap: luma_key_low and luma_key_high only do anything in custom mode - presets override them, which is why people nudge those sliders for ten minutes and watch nothing change. luma_key_falloff is ramp smoothness (0 = hard binary), and luma_key_low_soft / luma_key_high_soft are Blend-If style ramps at each end so the key eases in. Two inverts, and they're not the same: luma_key_invert_key flips the sampled channel before thresholding, luma_key_invert flips the finished matte.

key: difference - the classic VFX difference matte. Wire plate (the same shot, clean, no subject) and this keys what changed. It only works as well as your plate is aligned; a hand-held plate gives you garbage, and no slider fixes that.

grade - gain, gamma, clamp, invert. Gamma bends the greys while black and white stay put, which is what you want on a mushy matte.

grow / shrink - independent grow_left, grow_right, grow_up, grow_down plus grow_feather. Anisotropic on purpose: +4 left and −4 right widens the matte horizontally without touching its height. combine takes two mattes (mask and mask_b) through combine_op.

gradient - generates a matte from nothing: linear, radial or angular, with angle, normalised centre and start/end values, sized by gradient_width/gradient_height or by size_as. grain adds noise via grain_amount, grain_size and a fixed seed - upstream reseeded every frame and the matte crawled, and a crawling matte is worse than no grain at all. motion blur smears the alpha along motion_blur_angle by motion_blur_distance, so the matte moves like the shot does.

edge spread - pushes inward colour outward by N pixels: the fix for a dark fringe when you cut a subject off a dark background and put it on a light one. shuffle is R/G/B/A channel shuffle with an optional premultiply output.

Inputs and outputs that matter

Wire what the mode needs: image for the keys, edge spread and shuffle; mask for grade, grow/shrink, combine, grain, motion blur and edge spread; mask_b for combine; plate for the difference key; size_as for gradient. Because the frontend hides irrelevant widgets, the first-time experience is "where's the slider I need" - it's under a different mode.

mask is the matte. image is the processed picture for edge spread and shuffle, and a grey preview of the matte otherwise. report tells you what it clamped or resized.

Installing it

ComfyUI Manager → search "CustomNodePacks", or clone it:

cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks

opencv-python and scipy are the real requirements, and the README warns you not to blind-install requirements.txt - it can clobber ComfyUI's bundled torch and numpy. Check what you already have with pip list | grep -i "opencv\|scipy\|safetensors". No checkpoints, no SAM weights, nothing to download: this node is pure tensor maths.

Where it bites

  • A luma key that swallows the whole frame is usually the preset keying the wrong brightness band - and nudging luma_key_low/high won't help until you switch luma_key_mode to custom.
  • Keys with dirty edges usually want colour_key_soft_falloff above zero, not more tolerance.
  • If the matte size doesn't match your image batch the node resizes what you gave it rather than erroring - the report string tells you when, and it's the first thing to read before blaming the keyer.
  • Directional grow is easy to cancel by accident: equal and opposite numbers left and right leave the vertical axis untouched, which is correct and confusing the first time.
Category🐺 C2C/🧰 Core/Mask

Inputs (65)

NameTypeDefaultDescription
modeCOMBOkey: colourWhat the node does. Each mode shows only its own controls.
colour_key_colorSTRING#FFFFFFkey: colour: Target colour as #RRGGBB — the hue you want to keep or remove.
colour_key_colorspaceCOMBOrgbkey: colour: Distance metric. LAB separates dark navy from dark brown; RGB treats them as similar luminance.
colour_key_toleranceFLOAT500–100key: colour: How far a pixel's colour can drift from the pick and still count as a match.
colour_key_soft_falloffFLOAT100–100key: colour: Graded band outside tolerance — stops a colour key from cutting like a pair of scissors.
colour_key_invertBOOLEANfalsekey: colour: Swap matched and unmatched regions.
luma_key_modeCOMBOautokey: luma: Preset luminance range or custom thresholds. auto: Analyzes image brightness to pick best range. highlights: Keys bright regions (0.7–1.0). midtones: Keys mid-range luminance (0.3–0.7). shadows: Keys dark regions (0.0–0.3). custom: Uses the low/high sliders directly.
luma_key_lowFLOAT0.000–1key: luma: Low threshold – pixels with luminance below this become 0 in the mask. Only used directly in custom mode; presets override this.
luma_key_highFLOAT1.000–1key: luma: High threshold – pixels with luminance above this become 1 in the mask. Only used directly in custom mode; presets override this.
luma_key_gammaFLOAT1.000.01–10key: luma: Gamma correction applied after keying. >1 compresses mask toward black (reduces coverage). <1 expands mask toward white (increases coverage).
luma_key_falloffFLOAT1.00–10key: luma: Smoothness of the transition between low and high thresholds. 0 = hard binary edge. 1 = standard smooth. >1 = very gradual transition.
luma_key_invertBOOLEANfalsekey: luma: Invert the output mask (swap keyed and unkeyed regions).
luma_key_channelCOMBOlumakey: luma: Channel to key on. Luma is BT.709; hue/sat/value and LAB a/b isolate coloured spill without three separate nodes.
luma_key_low_softFLOAT0.000–1key: luma: Blend-If split below low — ramp into the key instead of a hard cut that looks like a sticker.
luma_key_high_softFLOAT0.000–1key: luma: Blend-If split above high — feather the highlight side of the range.
luma_key_invert_keyBOOLEANfalsekey: luma: Invert the sampled channel before thresholding (ImageToMask behaviour). Distinct from invert, which flips the finished matte.
difference_key_toleranceFLOAT0.0800–1key: difference: How far a pixel may differ from the clean plate and still count as background (0..1 of the RGB distance).
difference_key_softnessFLOAT0.0500–1key: difference: Width of the soft edge above the tolerance.
difference_key_invertBOOLEANfalsekey: difference: Key the unchanged part instead of what changed.
grade_gainFLOAT1.000–16grade: Multiply the matte (above 1 pushes greys toward white).
grade_gammaFLOAT1.000.05–8grade: Bend the greys: above 1 lifts them, below 1 sinks them; black and white stay put.
grade_clampBOOLEANtruegrade: Keep the result inside 0..1.
grade_invertBOOLEANfalsegrade: Flip the matte at the end.
grow_leftINT0-4096–4096grow / shrink: Pixels to grow the matte toward the left (negative shrinks its left edge).
grow_rightINT0-4096–4096grow / shrink: Pixels to grow toward the right (negative shrinks its right edge).
grow_upINT0-4096–4096grow / shrink: Pixels to grow upward (negative shrinks its top edge).
grow_downINT0-4096–4096grow / shrink: Pixels to grow downward (negative shrinks its bottom edge).
grow_featherINT00–1000grow / shrink: Soften the result by this blur radius in pixels.
combine_opCOMBOunioncombine
gradient_widthINT5124–16384gradient: Output width when size_as is not connected.
gradient_heightINT5124–16384gradient: Output height when size_as is not connected.
gradient_typeCOMBOlineargradient: Ramp shape — linear edge fade, radial vignette, or angular wipe.
gradient_angleFLOAT0.0-360–360gradient: Direction for linear, or rotation offset for angular ramps.
gradient_center_xFLOAT0.500–1gradient: Radial/angular origin, normalised 0–1 across the frame.
gradient_center_yFLOAT0.500–1gradient: Radial/angular origin, normalised 0–1 down the frame.
gradient_startFLOAT0.000–1gradient: Mask value at the ramp start (black end).
gradient_endFLOAT1.000–1gradient: Mask value at the ramp end (white end).
grain_amountINT60–127grain: Noise strength — enough to hide banding, not enough to read as texture.
grain_seedINT00–2147483647grain: Fixed seed holds the grain still through a sequence; upstream reseeded every frame and the matte crawled.
grain_sizeINT41–64grain: Noise frequency — larger values give coarser, less speckly grain.
grain_invertBOOLEANfalsegrain: Flip mask polarity before adding grain.
motion_blur_angleFLOAT0.0-360–360motion blur: Blur direction in degrees, 0 = horizontal right.
motion_blur_distanceINT200–9999motion blur: Blur length in pixels along the angle.
motion_blur_invertBOOLEANfalsemotion blur: Flip mask polarity before blurring.
edge_spreadINT40–9999edge spread: How many pixels to push inward colour outward — kills the dark fringe on a lighter background.
edge_spread_invert_maskBOOLEANfalseedge spread: Flip mask polarity when your matte is white-on-black.
shuffle_out_RCOMBORshuffle
shuffle_out_GCOMBOGshuffle
shuffle_out_BCOMBOBshuffle
shuffle_out_ACOMBOAshuffle
shuffle_premultiply_outputBOOLEANfalseshuffle
imageoptIMAGEkeys, edge spread and shuffle: the picture to work on.
maskoptMASKgrade, grow / shrink, combine (first matte), grain, motion blur, transform; edge spread's matte; gradient's size when nothing else gives one.
mask_boptMASKcombine: the second matte.
plateoptIMAGEkey: difference - the clean plate (same shot, without the subject).
size_asoptIMAGEgradient: take the size from this image.
transform_expand_xoptINT0-512–512transform: Positive = dilate horizontally, Negative = erode horizontally
transform_expand_yoptINT0-512–512transform: Positive = dilate vertically, Negative = erode vertically
transform_blur_xoptFLOAT0.00–128transform: Gaussian blur sigma along X axis
transform_blur_yoptFLOAT0.00–128transform: Gaussian blur sigma along Y axis
transform_offset_xoptINT0-4096–4096transform: Shift mask horizontally (pixels)
transform_offset_yoptINT0-4096–4096transform: Shift mask vertically (pixels)
transform_featheroptFLOAT0.00–128transform: Feather (smooth edge) radius after transform
transform_thresholdoptFLOAT0.500–1transform: Binarize mask after morph ops (0 = keep soft)
transform_invertoptBOOLEANfalsetransform: Invert output mask

Outputs (3)

NameTypeDescription
maskMASKThe matte (shuffle: the new alpha; edge spread: the matte it used).
imageIMAGEedge spread / shuffle: the processed picture. Other modes: the matte as a grey image, to look at.
reportSTRINGWhat the node did, and anything worth knowing (clamped values, resized inputs).