cv2.find4QuadCornerSubpix
Refining Chessboard Corners with cv2.find4QuadCornerSubpix
- img
- corners
- region_size
- bool
- nparray
Chessboard detection gives you corner coordinates at pixel resolution. Camera calibration wants sub-pixel, because the whole point of a calibration is that a fraction of a pixel in each of a few hundred corners turns into a focal length you can actually trust. cv2.find4QuadCornerSubpix is one of the two refiners OpenCV gives you for that job, and it's the one with the smallest number of ways to go wrong.
What it is
A raw wrapper - one of the roughly 470 cv2.* nodes this pack generates at import time straight from the OpenCV type stubs, which is why it lives under image/CV/low-level/cv2 F and why its description is the OpenCV docstring and not a designed UI. Auto-generated means uncurated: the wrapper guarantees the signature, not that your parameters make sense.
Mechanically it re-locates each already-detected inner corner by looking at the intensity pattern of the four board squares that meet there - hence "4 quad" - inside a fixed window you choose (region_size). One pass over the corner list, no convergence loop.
Inputs and outputs
img is the board view, same image the detector saw. corners is the list your detector returned: it's a data array, and this socket only accepts an NPARRAY link - the pack is explicit about that in the input's tooltip, because feeding it an IMAGE would be a silent type lie. Wire it from cv2.findChessboardCorners, cv2.findChessboardCornersSB, or any other corner producer that emits an Nx1x2 array.
region_size is the one to think about. It's a two-component Size value (w, h), and the widget default is (0, 0), which is not a window. Set it, and keep it small: 5×5 is the usual pick for a board that fills the frame. Too large and you're averaging in neighbouring squares; too small and you're fitting noise.
The outputs are bool - cv2's return value for the call - and nparray, the refined corner array in the same Nx1x2 layout, in the same order, so it drops straight into everything downstream.
How it fits with the other refiner
OpenCV's everyday choice is cornerSubPix, which iterates against a termination criteria (max iterations, epsilon). This pack exposes that too as cv2_cornerSubPix, with the criteria split into criteria_type, criteria_max_count and criteria_epsilon widgets. find4QuadCornerSubpix trades that iteration control for a simpler story: a region size, one shot.
The practical question is whether you need either. If you're doing a normal calibration through the curated CV Calibrate Camera (Chessboard) node, you don't - that node finds the corners and refines them to sub-pixel internally, and skips views where the board wasn't found. Same for the fisheye calibrator. You reach for the raw refiner when you're building the chain by hand: detect, refine, then pair the result with CV Grid Points for the matching planar object points and hand both to cv2_solvePnP or cv2_calibrateCamera yourself.
Install
Comes with the pack, nothing extra to download.
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
ComfyUI Manager users: search "ComfyUI CV", install, restart. Needs Python ≥ 3.12 and a ComfyUI recent enough to have the V3 node API.
Common issues
cv2 error about the corners argument. The array shape is the usual culprit. Detectors emit Nx1x2; if something upstream gave you a flat Nx2 or a list, reshape it with the pack's array nodes before the refiner sees it. Reading the shape first with Inspect CV Data takes five seconds and saves the guessing.
Corners get worse, not better. Check region_size. This refinement assumes the detector's corner is already within about a pixel of the truth; if it's a few pixels off - motion blur, glare, a board at a steep angle - you're now fitting the inside of a wrong neighbourhood. Reject that view instead; a calibration with six clean views beats one with fifteen sloppy ones.
Dependency surprises. The pack pins opencv-contrib-python-headless~=5.0.0.93, and the behaviours are curated against that build. If another custom node in your install pulled in a non-contrib OpenCV wheel, the contrib submodules go empty and nodes vanish. tools/repair_opencv_contrib.py --check in the pack tells you whether that's what happened.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| img | NPARRAY,IMAGE,MASK | - - - 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. | |
| corners | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| region_size | CV_TUPLE | 0,0 | 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. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| bool | BOOLEAN | — |
| nparray | NPARRAY | — |