CV Calibrate Camera (Circle Grid)
The target OpenCV actually recommends for wide lenses
- images
- camera_matrix
- dist_coeffs
- rms_error
- views_used
- found
Chessboards are the default target everyone prints, and circles are the one the OpenCV docs quietly prefer for the hard cases. This node does the same job as CV Calibrate Camera (Chessboard) - intrinsics and lens distortion from a batch of views - using cv2.findCirclesGrid on a circle grid instead of corner detection.
Why bother: circle centres can be located to sub-pixel accuracy even when the target is blurred, and a symmetric grid of circles has a 180° rotational ambiguity that will happily halve your orientation. The asymmetric (staggered) layout - each row offset by half a spacing - kills that ambiguity outright, which is why staggered circle grids are the standard target for wide-angle and stereo rigs. This node defaults to it, and it's the right default.
How it works
Identical shape to the chessboard node: feed an IMAGE BATCH, each frame is grayscaled, circle centres are found and matched to planar object points built from the pattern size multiplied by square_size, and cv2.calibrateCamera fits intrinsics and distortion across everything collected. Views with no detectable grid are skipped. The reason this node exists at all is that calibration needs per-view 2D/3D correspondences accumulated across images, which the pack's raw cv2.* wrappers simply cannot express - so the accumulation lives inside the node.
Inputs
images- batch of views from different angles. 3 usable minimum, ~10–20 for a good fit.pattern_cols/pattern_rows- circle centres per row and per column (not squares, not cells). Note the defaults: 4 × 11. That's a staggered layout, and a 4 × 11 asymmetric grid is a classic OpenCV calibration target. If you feed it a symmetric grid but leave the layout on asymmetric, nothing is found - the geometry genuinely doesn't match.grid_layout- symmetric or asymmetric (staggered). Staggered unless you have a very good reason.square_size- centre-to-centre spacing of the circles, in whatever unit you care about. It sets the world scale, so the same advice as every other calibration node applies: measure the printed target. Leave 1.0 if you only need relative geometry.
Outputs
camera_matrix (3×3 K), dist_coeffs, rms_error (mean reprojection error in pixels - under ~1 is good), views_used, and found.
From here the matrix goes into cv2.undistort, into a pose solve, into CV Camera Pose To 3D View for a calibrated 3D preview camera - or into the pack's stereo-calibration node as an initial guess, which is where a staggered circle grid genuinely earns its keep.
Failure is soft again: fewer than 3 usable views gives found=false with an identity matrix and zero distortion. Identity looks like a working pinhole camera and is not one, so branch on found.
Install
Manager → 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"
Python ≥ 3.12 and a recent ComfyUI on the V3 node API. No downloads - you print the target.
Where people get burned
The pattern you printed versus the pattern you described. pattern_cols/pattern_rows must be the counts in the staggered arrangement if the layout is staggered; a staggered target described as symmetric is a guaranteed zero detections. Count the dots, not the paper.
Bad prints. Circles must be crisp with contrast; a laser printer's toner speckle is fine, a smudged inkjet is not. Same for illumination: circles go soft under glare, and a calibration with soft edges gives you a confident low RMS with a slightly wrong answer. Matte paper, diffuse light, enough views at different tilts, and check views_used against your batch size every time.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | Batch of circle-grid views from different angles (>= 3 usable; ~10-20 gives a good fit). | |
| pattern_cols | INT | 42–40 | Circle centers per row (width of the grid). |
| pattern_rows | INT | 112–40 | Circle centers per column (height of the grid). |
| grid_layout | COMBO | asymmetric (staggered) | Grid geometry. Asymmetric staggers each row by half a spacing, which removes the 180-degree rotational ambiguity of symmetric targets - the reason stereo and wide-angle calibration prefer it. |
| square_sizeopt | FLOAT | 1.000.0001–1000000 | Physical center-to-center spacing (e.g. mm); sets the world scale. Leave 1.0 for relative calibration. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| camera_matrix | NPARRAY | 3x3 intrinsic matrix K (fx, fy, cx, cy). |
| dist_coeffs | NPARRAY | Distortion coefficients (k1, k2, p1, p2, k3). |
| rms_error | FLOAT | Mean reprojection error in pixels; lower is better (<1 good). |
| views_used | INT | Number of input views with a detectable grid. |
| found | BOOLEAN | — |