Nodes/ComfyUI CV/CV Calibrate Camera (ChArUco)
ComfyUI Node

CV Calibrate Camera (ChArUco)

The case where a half-visible board still counts

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Calibrate Camera (ChArUco)
  • images
  • camera_matrix
  • dist_coeffs
  • rms_error
  • views_used
  • corners_used
  • found
◄dictionaryDICT_6X6_250►
◄cols5►
◄rows7►
◄cell_size0.040►
◄marker_ratio0.70►
◄min_corners6►
◄legacy_patternfalse►

The chessboard calibration node has one brutal requirement: a view is thrown away unless every inner corner is visible. That sounds reasonable until you try to shoot the corners of the frame - which is exactly where lens distortion is estimated from, and exactly where a full board doesn't fit.

ChArUco fixes that. A ChArUco board is a chessboard with ArUco markers embedded in the white squares, and a view contributes whatever corners it can identify. The markers tell you which corner is which, so a board running off the edge of frame, or half-hidden behind something, still contributes usable correspondences. Better photographs, fewer of them, and coverage of the image corners that a chessboard can't practically give you.

How it works

It detects the markers on each frame in the batch, interpolates the chessboard corner positions from them, and accumulates those corners as the 2D half of the calibration correspondences; the 3D half comes from the board geometry you specify. Corners land on the chessboard intersections, which localise to sub-pixel accuracy - better than marker corners alone, which is the whole reason the hybrid exists rather than just using a GridBoard. Views with too few identified corners are skipped. Then cv2.calibrateCamera does the fitting.

Inputs

Six required, and five of them are just the description of the board you printed:

  • images - a batch from different angles and distances. 3 usable views is the floor, ~15 gives a good fit. Tilt the board: all-fronto-parallel shots can't separate focal length from distance.
  • dictionary - the dictionary the markers were drawn from. Must match, or nothing is found.
  • cols / rows - chessboard squares across and down, as given to CV ArUco Board Image.
  • cell_size - physical square size of the printed board. Sets the world unit of every translation downstream.
  • marker_ratio - marker edge as a fraction of the square, i.e. the same 0.7 you generated the board with.

The optional pair: min_corners (default 6) skips views that identify fewer corners than this - 4 is the theoretical minimum for a planar view, but 6–10 keeps the fit stable and stops a nearly-blank frame from dragging it; and legacy_pattern, which you set only for boards generated by OpenCV before 4.6 (their top-left cell is white). Get that flag wrong on an old board and the corner IDs shift.

If you generated the board with this pack, square_length and marker_length from CV ArUco Board Image are literally the values to use here - that's why the board node outputs them as numbers.

Outputs

camera_matrix, dist_coeffs, rms_error, views_used, corners_used, and found.

corners_used is the one that really drives the fit - point count, not photo count - and it's the number to watch when RMS is poor. views_used versus your batch size tells you how many frames got skipped. Under ~1 px RMS is a good result.

Failure is soft: fewer than 3 usable views returns found=false with an identity matrix and zero distortion. Identity is a plausible-looking camera with no focal length, so gate the branch rather than letting it flow into a pose solve.

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, recent ComfyUI on the V3 API - and because this is all cv2.aruco under the hood, the contrib wheel is not optional. Install plain opencv-python on top of contrib and the ArUco (and thus ChArUco) nodes vanish from the menus.

Where people get burned

Print scale. cell_size has to describe the paper in your hand. Print at 100% and measure; scale-to-fit is how you get a calibration that's off by 8% and looks perfect.

Gloss and glare. ChArUco detection is more forgiving than a plain chessboard, but a glossy print under a hard light will still lose markers to specular highlights. Matte paper, diffused light. The board node's margin quiet-zone exists for the same family of reasons - markers need white space around them to be found reliably.

Categoryimage/CV/features

Inputs (8)

NameTypeDefaultDescription
imagesIMAGEBatch of board photos from DIFFERENT angles and distances (>= 3 usable; ~15 gives a good fit). Tilt the board - views that are all fronto-parallel cannot separate focal length from distance.
dictionaryCOMBODICT_6X6_250Dictionary the board's markers were drawn from.
colsINT52–50Chessboard squares across (as given to the board image node).
rowsINT72–50Chessboard squares down.
cell_sizeFLOAT0.0400.0001–1000000Physical square size of the PRINTED board; it sets the world unit of the translation vectors.
marker_ratioFLOAT0.700.05–0.95Marker edge as a fraction of the square - the value used to generate the board.
min_cornersoptINT64–1000Skip a view that identifies fewer corners than this. Four is the theoretical minimum for a planar view; 6-10 keeps the fit stable.
legacy_patternoptBOOLEANfalseSet for boards generated by OpenCV before 4.6 (white top-left cell).

Outputs (6)

NameTypeDescription
camera_matrixNPARRAY3x3 intrinsic matrix K (fx, fy, cx, cy).
dist_coeffsNPARRAYDistortion coefficients (k1, k2, p1, p2, k3).
rms_errorFLOATMean reprojection error in pixels; lower is better (under ~1 is good).
views_usedINTHow many input views contributed corners - compare it with the batch size to see how many were skipped.
corners_usedINTTotal identified corners across all used views; this is the number that really drives the fit.
foundBOOLEAN—