cv2.approxPolyDP
Turn a 400-point contour into four corners
- curve
- nparray
A contour out of findContours is usually hundreds of points, because a raster boundary wobbles by a pixel wherever it goes. If what you wanted was "this blob is a rectangle", you need the polygon underneath the wobble. That's approxPolyDP - the Douglas–Peucker simplification, and the standard way to answer "how many sides does this shape have?".
It's the workhorse behind document scanning, sticker detection, quad finding and any pipeline that needs four corners to warp from.
How it works
Douglas–Peucker walks the point chain and throws away every point that lies within epsilon pixels of the line between the points it kept. Small epsilon keeps detail; large epsilon keeps corners. At the extreme it collapses to a triangle or a line, which is a signal you went too far.
Crucially, this node takes a point set, not an image. curve is NPARRAY-only, and the tooltip says so outright: a data array, not pixels. Image sockets are not offered here, and that's deliberate - the wrapper's typing knows the difference between a picture and a polygon.
The output is a point array again, so nothing in this node is directly previewable. You look at polygons by drawing them, not by reading them.
Inputs and outputs
curve(NPARRAY) - an ordered point chain. The pack's CV Contour To Points bridgesCV_CONTOURSfrom CV Find Contours (optionally after CV Select Contour) into the array type this wants.epsilon(FLOAT) - maximum allowed distance between the original curve and its approximation, in pixels.closed(BOOLEAN) - whether the curve's first and last vertices are connected.- nparray - the simplified point chain.
Pick epsilon as a fraction of the perimeter: cv2_arcLength gives you the perimeter, and 1–2% of it is the usual starting point (perimeter 800 → epsilon 8–16). Picking a fraction of the image width instead is the classic beginner mistake - a big object tolerates a bigger epsilon than a small one.
What you wire it into
- Rectangle / quad detection. Simplify, then count: a clean 4-point result plus
cv2_isContourConvexis a quadrilateral. The pack's curated CV Find Quadrilateral does that job end-to-end, with the largest-quad selection andfoundflag handled for you. - Drawing a clean outline.
nparray→ CV Points To Contour → CV Draw Contours, or straight into CV Draw Polygon. The wobble disappears and the overlay reads as a shape. - Shape classification. Feed the vertex count and the polygon's area into your own logic; the pack's CV Filter Contours By Shape compares against a reference shape with a selectable invariance policy if you'd rather not hand-roll the metric.
- Fitting a polygon for IoU checks. The CV Polygon IoU subgraph does exactly this, and it starts with simplification.
Install
ComfyUI Manager → comfyui_cv (ComfyUI CV), or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart ComfyUI. Needs Python ≥ 3.12 and a recent ComfyUI on the V3 node API; behaviour is curated against OpenCV 5.0.0.93. Keep the contrib wheel - non-contrib installs silently drop the contrib submodules.
Where people get burned
epsilon in the wrong units. It's pixels, not a percentage, and not a 0–1 fraction. A contour perimeter of 2000 needs an epsilon in the tens to simplify at all.
closed mismatch. With closed=False the gap between the last and first point isn't approximated, so a closed shape can keep one extra vertex at the seam. If your square keeps coming out with five points, check this first.
Point shape and dtype. OpenCV wants a point chain it can read as 2D points (an Nx1x2-ish array of int32/float32). The pack's contour bridge produces a suitable array; if you assembled points yourself with CV Points / CV Concat Arrays, check with CV Array Shape and fix with CV Reshape Array / CV Cast Array. This is where most "assertion failed" errors here come from.
Wondering why epsilon = 0 changed nothing. It can't: nothing is within zero of the line except the points on it. Start at a couple of pixels.
Expecting it to return a property. It returns points. If you want the area, length or corner count, those live in cv2_contourArea, cv2_arcLength and CV Shape Moments respectively.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| curve | NPARRAY | Input vector of a 2D point stored in std::vector or Mat A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| epsilon | FLOAT | 0.0000-1e+38–1e+38 | Parameter specifying the approximation accuracy. This is the maximum distance between the original curve and its approximation. |
| closed | BOOLEAN | false | If true, the approximated curve is closed (its first and last vertices are connected). Otherwise, it is not closed. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |