cv2.fitEllipseDirect
The ellipse fit that won't hand you nonsense
- points
- ellipse
Ellipse fitting has a dirty secret: the obvious least-squares approach can return something that isn't an ellipse. Give the classic cv2.fitEllipse a point set clustered on one arc, or a nearly-straight smear of pixels, and the fit is under-constrained - you get a degenerate or absurdly stretched rotated rect, and cv2 says nothing. cv2.fitEllipseDirect is the direct least-squares formulation that constrains the problem so the solution is always a real ellipse. It's the one to use when the fit feeds something automated and a garbage answer would travel silently downstream.
It's one of the raw OpenCV wrappers in ComfyUI CV (bmad4ever) - the pack that exposes OpenCV 5.0 to ComfyUI as ~470 generated cv2.* nodes plus a curated layer. Same author, same blunt README: heavy LLM assistance in development, explicitly not recommended for production use without your own code review. For a single cv2 call wrapped with typed sockets, you're getting exactly the thing you'd write yourself.
Inputs, outputs
One input. points - NPARRAY, and only NPARRAY: the tooltip is explicit that this is a data array, not pixels, so an IMAGE link is not accepted. OpenCV wants Nx1x2 or 1xNx2 floats and needs five points minimum. Get one from CV Contour To Points, from a feature/keypoint pipeline, or from CV Annotate Points if you're measuring a photo by hand; CV Reshape Array is the node to fix shape problems before they become cv2 errors.
One output. ellipse, on a CV_ROTATED_RECT socket, nested as ((cx, cy), (width, height), angle_degrees). The author's tooltip names its intended destinations - cv2.ellipse's box input, cv2.boxPoints, cv2.rotatedRectangleIntersection - and because the type is its own socket, a downstream node either accepts a rotated rect or it doesn't; there's no flattening into a five-number list and no silent re-interpretation. CV Draw Ellipses is the drawing route.
Choosing between the three fits
All three nodes take the same input and emit the same socket, so switching is a rewiring, not a rewrite. The difference is the estimator:
cv2.fitEllipse- the classic, fastest, least-squares fit. Perfect on a full, evenly-sampled outline. Prone to degenerate answers on partial arcs and near-collinear points.cv2.fitEllipseAMS- approximate mean-square, aimed at unevenly sampled points (a half-occluded rim, a contour denser on the lit side). A good middle ground.cv2.fitEllipseDirect(this one) - direct least-squares, constrained so the output is guaranteed to be an ellipse. The stable choice for batch work.
The practical test is cheap: run two of them on the same points array and compare centre and axes. Wide disagreement means the input is bad, not the estimator - and the input being bad is usually a contour with holes or a couple of stray pixels attached. CV Filter Contours (keep by measured properties), CV Merge Contours (close small gaps), and CV Keep Largest Component are the cleanup nodes that fix the cause rather than the symptom.
Install
ComfyUI Manager → ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install -r comfyui_cv/requirements.txt
Needs Python ≥3.12 and a current ComfyUI (the pack is built on the V3 node API). Restart ComfyUI after installing. The one dependency is opencv-contrib-python-headless~=5.0.0.93.
Heads-up on ComfyUI Manager: the pack's registry name is ComfyUI CV, not "OpenCV" anything - it's deliberately not named after the library, because OpenCV is a registered trademark and this is an independent wrapper. Searching for "opencv" finds you other packs and a lot of confusion.
Common issues
A cv2 error naming the points array. Fewer than five points is the usual answer; the wrapper prints the argument shapes it passed, so check them before assuming a bug.
Fits that look "inside out" on thin shapes. A bright thin ring gives contour points on both inner and outer edges, and the fit lands between them. Split the contour or filter by area first (CV Filter Contours).
Nothing to feed it. You need points, not an image. If all you have is a mask of a roundish blob, the route is CV Find Contours → CV Select Contour (largest, say) → CV Contour To Points → this node. Alternatively, skip ellipse fitting altogether when the question is "how round is it": CV Region Properties can emit an equivalent ellipse and roundness-style columns per region without a fitting node in the chain.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| points | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| ellipse | CV_ROTATED_RECT | - - - Rotated rectangle ((center_x, center_y), (width, height), angle in degrees) - wire it into cv2.ellipse's box, cv2.boxPoints or cv2.rotatedRectangleIntersection. |