cv2.estimateChessboardSharpness
Is your calibration shot actually in focus?
- image
- patternSize
- corners
- sharpness
- sharpness_map
This is one of the most narrow-purpose functions OpenCV ships, and it's genuinely nice to have in a node graph for the ten minutes a year you need it. It tells you whether a chessboard calibration photo is sharp enough and evenly lit enough to bother feeding to a calibrator. If you're not shooting a checkerboard, close the tab - nothing here applies to your images.
What it measures
You hand it a gray image, the board's pattern size, and the corner set that findChessboardCornersSB already found. It then walks the edges between those corners and looks at the intensity profile across each one: a sharp edge transitions fast, a blurred or motion-smeared one ramps slowly.
The rise_distance parameter is the definition of "fast" - the default 0.8 means it measures the transition from 10% to 90% of the final signal strength, i.e. an edge rise time. A short rise is a crisp edge. Its average becomes the headline number, with the average minimum and maximum brightness alongside it, because the other classic way a calibration shot fails is being too dim or clipped in the highlights while looking fine on your screen. vertical chooses which set of edge responses to compute (False, the default, does horizontal lines), and the node also emits the sharpness map across the board as an NPARRAY, which is the "which part of the board is blurry" view.
Inputs and outputs
image- accepts an IMAGE, MASK or NPARRAY. It wants gray, so feeding a full-colour photo through the pack's smart typing is fine; feeding it a raw 3-channel NPARRAY is not.patternSize- aCV_TUPLE, so it renders as two integer widgets (w,h) in inner corners, not squares. A 9×6-square board is8 x 5. Get this wrong and the corner count won't match and the function throws.corners- NPARRAY only, and it has to be the output of the SB detector specifically; the tooltip names#findChessboardCornersSB. That'scv2.findChessboardCornersSBin this pack (withCV Chessboard Flagsto author the flags for the classic variant if you're using that one instead).sharpness- a STRING, spelled as the pack's Scalar literal:"(average sharpness, average min brightness, average max brightness, 0)". It's a formatted tuple, not a sensor reading, so read it or parse it, don't wire it into a numeric input expecting a float.sharpness_map- the NPARRAY map. Preview it withPreview CV ArrayorCV Color Mapif you want to see the blurry region as a picture.
Where it fits
Between shooting and calibrating. The pack's CV Calibrate Camera (Chessboard) will happily accept a batch of views and return intrinsics that are quietly wrong because three frames were hand-shaken or the board was lit from one side. Screening every candidate frame for sharpness and brightness before the calibrator is the cheapest possible fix for that, and it's the honest answer to "why are my intrinsics garbage?" - usually because a third of the inputs shouldn't have been in the batch.
It's also a decent sanity check for any board-based pipeline, not just calibration: if your corner detector is finding the board in a soft frame, the corner positions are implicated too, not just the calibration.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Or install comfyui_cv from ComfyUI Manager and restart. Python ≥ 3.12 and a recent V3-API ComfyUI. Node path: image/CV/low-level/cv2 E. Note that calibration workflows in this pack also want a handful of example inputs copied into ComfyUI/input - the README covers the one-node CV Install Example Inputs workflow for that, since ComfyUI's Load Image dropdown can only see what's already in your input folder.
Traps
The usual raw-wrapper ones plus calibration-specific ones. The corner set and patternSize must agree exactly or you get an argument error rather than a number. The board must be fully visible - a board running off the frame edge biases the average with missing data. And rise_distance is a definition, not a quality setting: changing it changes the number without changing the photo, so don't tune it to make your shots pass. If it says the frame is soft, the frame is soft; reshoot it and thank the node for saving you a calibration re-do.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | NPARRAY,IMAGE,MASK | Gray image used to find chessboard corners 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 | Size of a found chessboard pattern 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 | Corners found by #findChessboardCornersSB A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| rise_distanceopt | FLOAT | 0.8000-1e+38–1e+38 | Rise distance 0.8 means 10% ... 90% of the final signal strength Preset to the OpenCV default (0.8). |
| verticalopt | BOOLEAN | false | By default edge responses for horizontal lines are calculated Preset to the OpenCV default (False). |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| sharpness | STRING | Scalar(average sharpness, average min brightness, average max brightness,0) A cv2 Scalar as the pack's literal spelling "(c0, c1, c2, c3)" - one value per channel, and exactly what every colour / borderValue input takes. |
| sharpness_map | NPARRAY | — |