Nodes/H3 Relay/Prepare Mask Sequence (Euclidean Radius)
ComfyUI Node

Prepare Mask Sequence (Euclidean Radius)

Grow the mask in a circle, not a square

By akatz-ai·Created 2 months ago·Updated a day ago· 30
Prepare Mask Sequence (Euclidean Radius)
  • masks
  • images
  • generation_mask
  • mask_overlay
◄source_frames120►
◄expansion_px64►

Once you have a tracked person mask, you almost never want to use it as-is. H3 needs breathing room around a subject - outlines, hair, the shadow under a chin - or you get a regenerated person with a hard-edged aura of untouched background where the old silhouette used to be. So you expand it.

ComfyUI's usual GrowMask expands by a box: 64 pixels out means 64 pixels up, down, left and right, which is 64 pixels of growth on the axes and 90 diagonally. Prepare Mask Sequence (Euclidean Radius) expands by true circular radius instead: every pixel within expansion_px of the mask is included, and nothing further. It's a small difference you can see instantly on a diagonal edge - and the pack's tooltip says it's identical to the expansion the app uses, which is why it exists as its own node rather than a GrowMask with a tapered corner enabled.

What it does

For each frame it binarizes at 0.5, computes an exact Euclidean distance transform from the mask, and includes everything at distance ≤ expansion_px. Empty frames get left empty - the node doesn't invent coverage, it reports it.

That report is the good part. Before expanding, it checks every frame in the source range and if any frame has no selected pixels it refuses with a list of up to twenty frame numbers: SAM lost the selected person at frames 12, 13, 14, .... You can't miss it, and it names the exact frames you need to fix. That's a much better experience than silently generating a shot where your character vanishes for half a second. Fix those frames with Recover Empty SAM Frames or Refine Mask, then come back.

It also tail-pads: if images is longer than source_frames - which it always is, because Prepare Source pads to the legal H3 length - the last expanded mask is repeated out to the full image count. So the mask covers the padded frames the model will actually sample, without you hand-authoring junk frames.

Inputs and outputs

Required: masks (your tracked/selected mask sequence - a full-timeline MASK), images (the prepared frames, from Prepare Source's reference_frames, used for dimension checking and the overlay), source_frames (the unpadded count, from Prepare Source - get this wrong and the check is meaningless) and expansion_px, 0–256, default 64.

64 is a sane starting point at 480p and it's what the pack's character-swap graph uses. Push it up if you see remnants of the original outline; pull it down if H3 starts repainting background it should have left alone. Bigger masks mean more of the frame gets generated, which means more to get wrong.

Two outputs: generation_mask - the expanded sequence, ready for Window Inputs' mask input (Window Inputs slices it per window and hands it to Apply Edit Mask) - and mask_overlay, the prepared frames with the mask composited in a green tint at 35% opacity. Wire the overlay to a Preview Image. You are meant to look at it; every single time, before rendering.

Install

Manager → H3 Relay → restart, or:

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/akatz-ai/h3-relay.git

One real dependency heads-up: this node uses scipy (distance_transform_edt) and numpy. The pack's pyproject.toml declares no pip dependencies, so nothing pulls scipy in for you. Most ComfyUI environments have it; a minimal venv may not, and the failure is an ImportError when the pack's node module loads, not a helpful message. If the whole H3 Relay category is missing from the menu, check that first.

ComfyUI 0.37.0+. And the territory caveat applies to the H3 weights, not to this node: US, EU, UK and Korea are outside the MiniMax H3 community licence.

Where people get burned

Passing h3_length instead of source_frames is the mistake that looks fine and isn't: the node happily checks a range that doesn't exist, then refuses because those trailing frames are empty. source_frames is the real count; h3_length is the model's padded length.

The other one is subtler and more expensive. An oversized expansion is not free - it tells the sampler "regenerate all of this," including background H3 will happily reinterpret. If your joins look like the whole frame shifts between windows rather than just the subject, your mask is probably 40 pixels wider than it needed to be.

CategoryH3 Relay/mask tools

Inputs (4)

NameTypeDefaultDescription
masksMASK—
imagesIMAGE—
source_framesINT120—
expansion_pxINT640–256Outward Euclidean radius in prepared-video pixels; identical to the app's mask expansion.

Outputs (2)

NameTypeDescription
generation_maskMASK—
mask_overlayIMAGE—