cv2.drawChessboardCorners
The sanity check that saves your calibration
- source
- patternSize
- corners
- result
If you're heading anywhere near camera calibration - undistorting a lens, solving for a camera pose, AR placement on a photographed surface - this is the node you run before you trust the numbers. It draws the detected corner grid over your photo so you can see whether the detector actually found your board, and in what order.
What you see, and what it means
The OpenCV behaviour, straight from the tooltip: corners come out as red circles when the board wasn't found, and as coloured corners connected with lines when it was. Those two pictures are wildly different and instantly diagnostic.
The coloured case is the one you want. The colours run along the grid and the connecting lines make the ordering visible - corners are returned row by row, left to right, and if the detector picked up a corner out of sequence, you'll see the lines cross. That's the failure that ruins a calibration quietly, because the solve still converges, it just converges to something wrong.
The inputs
source is the image to draw on - an IMAGE, MASK or NPARRAY - and the result output echoes whatever you fed it.
patternSize is the trap this node exists to catch, and both the OpenCV docs and the pack's own reference material are explicit about it: it counts inner corners per row and column, not squares. A board printed with 8×8 squares has 7×7 = (7, 7) inner corners. Getting this wrong is the single most common beginner error in calibration, and the good news is that the picture tells you immediately - you'll get red circles instead of a grid, or a grid that only covers part of the board. It's a CV_TUPLE widget (width, height), typeable, or wireable from CV Tuple.
corners is not an image despite the NPARRAY socket: it's the corner array - the output of cv2.findChessboardCorners in the same pack, an (N, 1, 2) float array of subpixel positions. Wire them directly.
patternWasFound is the boolean from that same detector's found output. Feed it through honestly. If you hard-code it to true while the detector returned false, you're asking the node to draw connections between corners that don't exist.
The workflow it belongs to
The pack ships this exact pair as a playground: findChessboardCorners → drawChessboardCorners, on a set of synthetic views with distortion baked in, with a note that says the quiet part out loud - "non-chessboard images yield found=false and pass through unmarked." In other words, a broken detection is not an error, it's an unmarked image. If your preview comes back unchanged, the answer is almost always patternSize or a board that's too small in frame, not a bug.
The other end of the chain is the pack's CV Calibrate Camera (Chessboard), which takes a batch of views and returns intrinsics plus distortion. Detecting the corners in twenty views and eyeballing each overlay is tedious; detecting them once, checking the overlay, and only then feeding the batch is the version of the job that doesn't waste an afternoon.
It also matters outside pure calibration: a detected chessboard is a known 3D object in the scene, which is why the same corner set feeds solvePnP for pose and why the pack's 3D-preview workflow can composite a model onto a photographed board. Depth maps, stereo rectification, pose - all of it runs on the assumption that the detection was right. This is the node that checks the assumption.
Installing comfyui_cv
One of ~470 auto-generated raw cv2.* wrappers in bmad4ever/comfyui_cv, plus curated nodes - GPL-3.0, forked from Gerold Meisinger's opencv-comfyui. Manager: search ComfyUI CV. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart after. Python ≥ 3.12, a recent ComfyUI on the V3 node API, and one dependency: opencv-contrib-python-headless~=5.0.0.93. The contrib wheel is the one to keep - all four OpenCV distributions write to the same site-packages/cv2 and the last install wins, so a plain opencv-python strips the contrib nodes. tools/repair_opencv_contrib.py --check / --apply is the repair path. The pack's example_inputs/ folder has synthetic calibration boards you can run this on immediately.
Common issues
Red circles everywhere. Board not detected: wrong patternSize, low contrast, insufficient size in frame, or a pattern with too few inner corners for the detector's default flags. The pack also ships a CV Chessboard Flags node for the detection flags on findChessboardCorners.
Grid drawn on the wrong spots. patternSize mismatched to the board, or the corner array belongs to a different image than the one you're drawing on.
Nothing at all was drawn. The corner array is empty because the detector found nothing; found = false with no corners is a pass-through, not an error.
Wobbly corners. Expected - detection is approximate. cv2.cornerSubPix refines positions if you need subpixel accuracy.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| source | COMFY_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. | |
| patternSize | CV_TUPLE | 0,0 | Number of inner corners per a chessboard row and column (patternSize = cv::Size(points_per_row,points_per_column)). One value with 2 components (w, h) - it travels as a whole, so it cannot arrive half-connected. Wire it from 'CV Tuple' or type the components in place. |
| corners | NPARRAY | Array of detected corners, the output of #findChessboardCorners. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| patternWasFound | BOOLEAN | false | Parameter indicating whether the complete board was found or not. The return value of #findChessboardCorners should be passed here. The function draws individual chessboard corners detected either as red circles if the board was not found, or as colored corners connected with lines if the board was found. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| result | COMFY_MATCHTYPE_V3 | Echoes the 'source' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |