Nodes/ComfyUI CV/cv2.fillConvexPoly
ComfyUI Node

cv2.fillConvexPoly

Turn four points into a mask (mind the LINE_AA default)

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.fillConvexPoly
  • source
  • points
  • result
◄color(0, 0, 0, 0)►
◄lineTypeLINE_AA►
◄shift0►

Drawing a filled polygon is how you go from "these corner coordinates" to "a region I can mask, cut, or inpaint". It's the primitive behind every quad-based tool you've used - select four corners, get a surface. cv2.fillConvexPoly is the raw version of that, and two of its defaults are quietly wrong for mask work.

What it does, and the constraint in the name

You hand it a canvas, a point array, and a colour, and it fills the polygon formed by those points. The scanline fill means the whole interior is covered in one call, no per-pixel loop.

Convex isn't decoration. The function makes no attempt to detect or handle a concave outline; feed it one and you get a fill that crosses over itself - a plausible-looking shape, entirely wrong, with no error anywhere. If your region isn't convex, don't triangulate it by hand: this pack's CV Draw Contours fills contours and handles arbitrary outlines properly.

Inputs

  • source - the canvas, accepting an IMAGE, a MASK or an NPARRAY. It's required, so you always need an existing picture or mask to draw into; there's no "make me an empty canvas" behaviour. For a blank start, this pack has CV Constant Like, which builds a constant array shaped like another one - that's your white or black sheet.
  • points - NPARRAY only, the polygon vertices, N x 2. From CV Points if you're typing corners, from CV Image Corners for the four corners of the frame, or from a contour-approximation chain if you're deriving it.
  • color - a string literal: "(0, 255, 0)" BGR on colour images, or a bare number, which broadcasts across components. Here's the trap for masks: on a single-channel canvas only the first component is used, and the default "(0, 0, 0, 0)" therefore fills with 0. On a mask that means "black", which is not a visible change to anything. If you want the region marked in a mask, pass 255.
  • lineType - default LINE_AA. Anti-aliased, i.e. the boundary pixels are blended and your fill has a soft gradient edge. For a hard binary mask you want LINE_8; for a mask you'll feather anyway, LINE_AA is a gift.
  • shift - fractional bits in the vertex coordinates. 0 means integer pixels; a non-zero value lets you specify vertices at subpixel precision. Leave it at 0 unless you have subpixel coordinates and know the scaling.

The single output, result, echoes the source's format - MASK in / MASK out, IMAGE in / IMAGE out, so this drops into a mask chain without a bridge. The drawing happens on a copy; your input array isn't modified.

Where it fits

Geometry-driven masking. Selecting a quadrilateral region to crop or warp; blocking out a sign or a logo; seeding an inpaint by covering exactly the area you want the model to redo; building a rough mask from corner coordinates instead of painting, when the region is a plane in the photo. The pack's own annotated-GrabCut and quad-warp demos are this family of node: a shape drawn as data, then fed to segmentation or a warp.

It's the honest counterpart to automating detection. A detector gives you a region when you can describe the object; four points give you one when you can't, and for a person blocking out a wall, a poster or a screen, corners are faster and more truthful than any model. For the paste-back half of the job - masks getting generous overlap and blur so the composite doesn't look pasted - inpainting.md is the short version of the actual advice.

Installing it

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

ComfyUI Manager: search comfyui_cv, install, restart. Python ≥ 3.12 and a recent V3-API ComfyUI. Node path: image/CV/low-level/cv2 F.

Traps

Concave outlines, as above - that's the one that silently produces garbage. The LINE_AA default producing soft edges when you wanted binary, and the colour literal's first-component rule meaning your mask fill was a no-op. Points must be an N x 2 array, not a flat list; a malformed point array is the most common hard error. And if the fill lands somewhere unexpected, suspect the coordinate frame - a polygon specified in the original 1024-pixel space drawn onto a 512-resized canvas looks almost like a bug in the node.

Categoryimage/CV/low-level/cv2 F

Inputs (5)

NameTypeDefaultDescription
sourceCOMFY_MATCHTYPE_V3 The image output(s) echo this input's format. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
pointsNPARRAYPolygon vertices. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here.
colorSTRING(0, 0, 0, 0)Polygon color. cv2 Scalar as a literal, e.g. "(0, 255, 0)" (BGR) or "(0, 255, 0, 64)" (BGRA). A bare number broadcasts to every component, so "255" means (255, 255, 255, 255). Components past the target's channel count are ignored by OpenCV.
lineTypeoptCOMBOLINE_AAType of the polygon boundaries. See #LineTypes
shiftoptINT0-2147483648–2147483647Number of fractional bits in the vertex coordinates. Preset to the OpenCV default (0).

Outputs (1)

NameTypeDescription
resultCOMFY_MATCHTYPE_V3Echoes the 'source' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY.