Nodes/ComfyUI-My-Mask/Mask To Convex Mask
ComfyUI Node

Mask To Convex Mask

Fill Every Concavity in One Node

By jerrylongyan·Created 2 years ago·Updated 2 years ago· 4
Mask To Convex Mask
  • mask
  • MASK

You know that moment - you've got a mask that's all jagged. Gaps between an arm and a torso, notches where your brush strokes didn't quite connect, a shoulder that looks like a ziggurat. You feed it to an inpaint pass and the model either invents content in the notches or bleeds color around them. This node is the one-sentence fix: it takes any mask and returns the tightest solid shape that still contains it - the convex hull. Think of a rubber band snapped around every white pixel. Everything inside the band goes white; everything outside stays black.

What it does

So it doesn't grow your mask outward. That's the part people get wrong. A convex hull fills the dents, not the area around the shape - if you want expansion, you want a grow/blur node. This is for turning a wobbly hand-painted or auto-segmented shape into a clean, predictable blob. That matters more than it sounds. Mask-based inpainting is still the only route that leaves unmasked pixels bit-identical, which is the whole reason anyone still bothers, and the quality of that pass lives or dies on how clean your mask boundary is before the VAE ever sees it. A convex mask feathers evenly, composites predictably, and doesn't hand the model a concave pocket to fill on its own.

How it works

Under the hood it's a few lines of OpenCV, which is why it's instant and needs no models. The mask is squeezed to a single frame, cast to uint8, and run through cv2.findContours with RETR_EXTERNAL - outside boundary only, interior holes ignored. Every contour's points get stacked into one point cloud, cv2.convexHull finds the rubber band, and cv2.fillPoly paints it solid at 1.0. Out comes a fresh float32 mask, same size as what went in.

The inputs and outputs

One input, one output, that's the whole UI. mask (MASK) in - wire it from whatever makes masks in your graph: SAM, GroundingDINO, the mask editor, an invert. Out comes MASK, which plugs straight into anything downstream that eats a mask: mask blur/feather, InpaintModelConditioning, ImageCompositeMasked, or the VAE encode for an inpaint pass.

Installing it

Installing is the boring part. ComfyUI Manager → search "ComfyUI-My-Mask" → Install, restart, done. Or the manual route:

cd ComfyUI/custom_nodes
git clone https://github.com/jerrylongyan/ComfyUI-My-Mask

Restart ComfyUI. The pack declares numpy, opencv-python, and torch (>= 2.0) in its requirement file; Manager grabs opencv-python if it's missing and you already have the other two. No model files, no downloads, nothing to configure.

Where people get burned

All grounded in the actual code:

  • Disconnected blobs get bridged. The hull is computed over all white pixels at once, so two separate mask islands become one solid shape connecting them. A U-shaped mask comes back as a disc. Sometimes that's exactly the point; sometimes it's a disaster. Keep a copy of the original if you might want it back.
  • It's single-frame and CPU-only. The code does squeeze(0).numpy(), so feed it one mask at a time. A batched mask (batch > 1) throws, and a CUDA tensor will too. ComfyUI masks are normally CPU tensors of shape (B, H, W) with B=1, so it works out of the box - just don't hand it a video batch.
  • The output is hard-edged. It fills at a flat 1.0, so any feather you had is gone. Blur after this node, not before - the uint8 cast also truncates soft anti-aliased edges when detecting contours, so a pre-feathered mask loses its gradient before the hull is even computed.

If your mask is already a clean convex blob, this node does nothing useful and you should skip it. If it's a jagged mess you need to make solid before an inpaint, it's the whole fix in one node - two, if you count its sibling that only convexifies the bottom half.

CategoryMaskToConvexMask

Inputs (1)

NameTypeDefaultDescription
maskMASK

Outputs (1)

NameTypeDescription
MASKMASK