ComfyUI Node

Mask Refiner

Training-free edge cleanup with a stage-by-stage toggle list

By Code2Collapse·Created 6 months ago·Updated a day ago· 52
Mask Refiner
  • image
  • mask
  • mask
  • alpha
  • preview
  • info
presetbalanced
auto_edge_locktrue
subject_classgeneral
enable_hole_fillfalse
morph_opnone
enable_thin_recoverfalse
enable_joint_bilateralfalse
enable_guided_filtertrue
enable_dense_crffalse
enable_edge_snapfalse
cascade_passes0
feather_sigma0.0
gamma1.00
threshold0.00
enable_domain_transformfalse
enable_color_decontaminatefalse
enable_unsharp_alphafalse
enable_anti_aliasfalse
enable_chroma_lockfalse
enable_speck_removalfalse
enable_temporal_smoothfalse
advanced_overrides_json
enable_integrity_checkfalse
integrity_drop_threshold0.40
integrity_jump_threshold0.15

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.

CategoryC2C/Pipeline

Inputs (27)

NameTypeDefaultDescription
imageIMAGERGB guide. Required for all edge-aware stages.
maskMASKMask to refine (soft or hard).
presetCOMBObalancedPicks sensible numeric defaults for every enabled stage. Override with `advanced_overrides_json` if needed.
auto_edge_lockBOOLEANtruePin 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_classCOMBOgeneralTunes 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_fillBOOLEANfalse
morph_opCOMBOnone5 options: none, close, open, dilate, erode
enable_thin_recoverBOOLEANfalse
enable_joint_bilateralBOOLEANfalse
enable_guided_filterBOOLEANtrue
enable_dense_crfBOOLEANfalse
enable_edge_snapBOOLEANfalse
cascade_passesINT00–5
feather_sigmaFLOAT0.00–20
gammaFLOAT1.000.1–5
thresholdFLOAT0.000–1
enable_domain_transformBOOLEANfalseGastal & 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_decontaminateBOOLEANfalsePush 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_alphaBOOLEANfalseSharpen the soft alpha (α + amount·(α − gauss(α))).
enable_anti_aliasBOOLEANfalseSub-pixel boundary smoothing (bilinear up 2× → soft contrast → down).
enable_chroma_lockBOOLEANfalseWhen 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_removalBOOLEANfalseDrop foreground components below `speck_min_area` and fill background holes inside the subject.
enable_temporal_smoothBOOLEANfalseBidirectional alpha EMA across the batch dim (for VIDEO masks only). Removes flicker without lag.
advanced_overrides_jsonoptSTRINGOptional 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_checkoptBOOLEANfalseCompute 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_thresholdoptFLOAT0.400–1Relative coverage drop that flags a frame.
integrity_jump_thresholdoptFLOAT0.150–1Relative frame-to-frame coverage jump that flags a frame.

Outputs (4)

NameTypeDescription
maskMASKRefined mask, same (B,H,W) as input.
alphaMASKSoft alpha (same as mask before threshold).
previewIMAGERGB×alpha preview for quick visual diff.
infoSTRINGJSON describing which stages ran / were skipped.