Mask Refiner
Training-free edge cleanup with a stage-by-stage toggle list
- image
- mask
- mask
- alpha
- preview
- info
A coarse mask from SAM is rarely finished. It's got holes, fuzzy edges, background bleed, maybe a thin antenna that got eroded away. Mask Refiner (MEC) is the cleanup stage - a chain of up to eleven training-free refinement passes, each with its own toggle, that polish a mask toward production quality without loading a single model weight.
Think of it as the "make it actually usable" node you bolt onto the end of a segmentation. The pack's bigger MaskOps node has auto-polish built in; this one gives you the manual, stage-by-stage control for when auto isn't enough.
How it works
Stages run in fixed order, each gated by a toggle: hole-fill → morphology → thin-structure recover → joint bilateral → guided filter → DenseCRF → edge-snap → optional CascadePSP-style multi-pass → feather → gamma → threshold. Missing optional dependencies silently disable just their stage - the rest still run, which is a thoughtful design.
The on-by-default magic is auto_edge_lock (default on): it pins the mask to real image edges so it stops bleeding into the background, tuning the guided filter + edge-snap by subject_class. Pick face, hair, garment, object, or hard_surface and it adapts - hair gets thin-recovery and a soft band, hard surfaces get a tight band and max snap. This single toggle + subject picker does most of the work people are trying to do with DenseCRF, for free.
Everything else is opt-in per stage: enable_hole_fill, morph_op (close/open/dilate/erode), enable_thin_recover (saves hair strands and antennas), enable_joint_bilateral, enable_guided_filter, enable_dense_crf (sharpest edges, needs pydensecrf), enable_edge_snap, cascade_passes, plus finishers: feather_sigma, gamma, threshold.
There are also five presets - balanced, fast, hair, aggressive, crf_heavy - that fill in sensible numerics for every enabled stage, and an advanced_overrides_json for power users who want to override individual stage parameters (gf_radius, crf_iterations, speck_min_area, ...). The optional enable_integrity_check appends per-frame stats (coverage, abrupt drops, frame jumps) to the info JSON - a lightweight video QA.
Outputs
mask (the refined hard mask), alpha (soft alpha before thresholding - grab this if you want a soft matte), preview (RGB×alpha for a quick diff), and info (JSON listing which stages ran or were skipped - invaluable when a dep is missing and a stage silently didn't run).
Install and dependencies
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
pip install opencv-python>=4.7.0 scipy>=1.10.0
Optional deps unlock stages: opencv-contrib-python (joint bilateral + domain transform), scikit-image (thin recovery), scipy (hole fill/morphology helpers - already a pack dep), pydensecrf (DenseCRF). Each missing dep disables only its stage, so don't over-install - add pydensecrf only if you genuinely need CRF-sharp edges. Restart after install and confirm [MEC] Loaded ... in the console.
The habit worth forming: turn on auto_edge_lock, set subject_class, then inspect info to see what ran. Reach for the exotic stages (DenseCRF, domain transform) only when the cheap ones lose.
Inputs (27)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | RGB guide. Required for all edge-aware stages. | |
| mask | MASK | Mask to refine (soft or hard). | |
| preset | COMBO | balanced | Picks sensible numeric defaults for every enabled stage. Override with `advanced_overrides_json` if needed. |
| auto_edge_lock | BOOLEAN | true | Pin the mask to real image edges to stop bleeding into the background. Force-enables guided_filter + edge_snap with parameters tuned by `subject_class`. Recommended for any face / garment / product / hair workflow. |
| subject_class | COMBO | general | Tunes auto_edge_lock for the dominant subject: general — balanced edge protection face — tight 2-3 px band, high snap, no morph dilate hair — thin-recover ON, soft band, low snap garment — medium band, medium snap, close holes object — medium band, high snap, fill holes hard_surface— thin band, max snap, threshold > 0.5 |
| enable_hole_fill | BOOLEAN | false | — |
| morph_op | COMBO | none | 5 options: none, close, open, dilate, erode |
| enable_thin_recover | BOOLEAN | false | — |
| enable_joint_bilateral | BOOLEAN | false | — |
| enable_guided_filter | BOOLEAN | true | — |
| enable_dense_crf | BOOLEAN | false | — |
| enable_edge_snap | BOOLEAN | false | — |
| cascade_passes | INT | 00–5 | — |
| feather_sigma | FLOAT | 0.00–20 | — |
| gamma | FLOAT | 1.000.1–5 | — |
| threshold | FLOAT | 0.000–1 | — |
| enable_domain_transform | BOOLEAN | false | Gastal & Oliveira (2011) Domain Transform RGB-edge filter. Often sharper than guided filter on hair / fine detail; much faster than DenseCRF. Requires opencv-contrib-python (ximgproc). |
| enable_color_decontaminate | BOOLEAN | false | Push alpha in the boundary band toward 0/1 using local LAB-distance to fg/bg means. Fixes 'halo' alpha bleed when bg has similar luminance. |
| enable_unsharp_alpha | BOOLEAN | false | Sharpen the soft alpha (α + amount·(α − gauss(α))). |
| enable_anti_alias | BOOLEAN | false | Sub-pixel boundary smoothing (bilinear up 2× → soft contrast → down). |
| enable_chroma_lock | BOOLEAN | false | When fg/bg luminance is similar but chroma differs, weight the boundary by LAB chroma gradient instead of luma. Helps red-on-red, green-on-green, etc. |
| enable_speck_removal | BOOLEAN | false | Drop foreground components below `speck_min_area` and fill background holes inside the subject. |
| enable_temporal_smooth | BOOLEAN | false | Bidirectional alpha EMA across the batch dim (for VIDEO masks only). Removes flicker without lag. |
| advanced_overrides_jsonopt | STRING | Optional JSON overriding any preset numeric. Example: {"gf_radius":12, "jb_sigma_color":40, "crf_iterations":8}. Recognised keys: hole_fill_threshold, morph_radius, thin_threshold, thin_min_branch_len, thin_branch_dilate, jb_diameter, jb_sigma_color, jb_sigma_space, gf_radius, gf_epsilon, crf_iterations, crf_gauss_sxy, crf_bilateral_sxy, crf_bilateral_srgb, edge_snap_strength, edge_snap_band, dt_sigma_s, dt_sigma_r, decontam_band, decontam_strength, unsharp_sigma, unsharp_amount, anti_alias_strength, chroma_lock_strength, chroma_lock_band, speck_min_area, speck_fill_holes_below, temporal_alpha, temporal_bidi. | |
| enable_integrity_checkopt | BOOLEAN | false | Compute per-frame integrity stats on the refined mask (coverage, abrupt drops, frame-to-frame jumps) and append them to the `info` JSON. Adds negligible cost for single frames; cheap for short clips. |
| integrity_drop_thresholdopt | FLOAT | 0.400–1 | Relative coverage drop that flags a frame. |
| integrity_jump_thresholdopt | FLOAT | 0.150–1 | Relative frame-to-frame coverage jump that flags a frame. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| mask | MASK | Refined mask, same (B,H,W) as input. |
| alpha | MASK | Soft alpha (same as mask before threshold). |
| preview | IMAGE | RGB×alpha preview for quick visual diff. |
| info | STRING | JSON describing which stages ran / were skipped. |