calib-targets

Fast, robust calibration-target detection in Rust: chessboard, ChArUco,
PuzzleBoard, and checkerboard marker boards. This is the facade
crate — the one most users install. It re-exports every detector in the
workspace and adds one-call helpers that take an image::GrayImage, run
ChESS corner detection, and return a labelled grid.
Install-friendly entry for the workspace; each detector has its own crate with deeper documentation and tuning reference, linked below.
Book: https://vitalyvorobyev.github.io/calib-targets-rs/
Install
The image crate is re-exported as calib_targets::image (it ships with
the default image feature), so you do not need to add it separately.
Importing through the re-export guarantees your GrayImage type is exactly
the one the helpers accept — no version-drift expected GrayImage, found GrayImage surprises. A direct cargo add image dependency still works.
Quickstart (chessboard)
use ChessboardParams;
use detect;
use ImageReader;
Inputs (every helper)
image::GrayImage(or aGrayImageViewon the detector traits).- A
*Paramsconfig struct (ChessboardParams,CharucoParams,PuzzleBoardParams,MarkerBoardParams). Use::default()for chessboard,::for_board(spec)when a layout is required (constructors that store the board take it by value; thesweep_for_board(&spec)presets take it by reference). - For the
*_bestsweep helpers, a slice&[Params]— typically the 3-config presetChessboardParams::sweep_default()for chessboards, or{Charuco,PuzzleBoard,MarkerBoard}Params::sweep_for_board(&spec)for the board detectors.
Outputs
Every detector emits a typed result object with a corners vector.
Chessboard returns ChessboardCorner; ChArUco, PuzzleBoard, and marker
boards return target-specific corner structs that can be converted to
the shared TargetDetection carrier when needed. Each corner carries:
| Field | Meaning |
|---|---|
position: Point2<f32> |
Sub-pixel image location. |
grid |
(i, j) integer grid index, i right, j down, rebased so bounding-box min is (0, 0) where applicable. |
id |
Logical corner ID on targets that provide one (ChArUco marker-referenced, PuzzleBoard master ID). |
target_position |
Physical location on the printed board (mm / board units), when cell size and alignment are known. |
score: f32 |
Detector-specific quality score. |
Chessboard enforces two hard invariants on its output: no duplicate
(i, j) labels, and (0, 0) sits at the visual top-left of the
detected grid.
Supported targets
| Target | Facade helpers | Dedicated crate |
|---|---|---|
| Chessboard | detect_chessboard, detect_chessboard_all, detect_chessboard_best, detect_chessboard_with_diagnostics |
calib-targets-chessboard |
| ChArUco | detect_charuco, detect_charuco_with_corners, diagnose_charuco, diagnose_charuco_with_corners, detect_charuco_best |
calib-targets-charuco |
| PuzzleBoard | detect_puzzleboard, detect_puzzleboard_with_corners, diagnose_puzzleboard, diagnose_puzzleboard_with_corners, detect_puzzleboard_best |
calib-targets-puzzleboard |
| Marker board | detect_marker_board, detect_marker_board_with_corners, diagnose_marker_board, diagnose_marker_board_with_corners, detect_marker_board_best |
calib-targets-marker |
| Printable targets | printable::{render_target_bundle, write_target_bundle} |
calib-targets-print |
| ArUco / AprilTag primitives | aruco::* (dictionaries, matcher) |
calib-targets-aruco |
* gated behind the diagnostics feature.
Every detector ships a single-config helper and a 3-config
*_best sweep. The sweep is the recommended default for new callers:
it handles threshold tradeoffs without forcing manual tuning.
The _with_corners variant reuses one ChESS corner pass across several
target detectors on the same image; detect_t runs that pass itself
from params.chess, so it is the one-liner for the common case.
Main ideas
- Grid-first. Every detector reduces to "find a chessboard grid,
then decode anchors / dots / circles in rectified cells". The heavy
lifting lives in
calib-targets-chessboardandprojective-grid. - Precision-by-construction. Wrong
(i, j)labels would corrupt calibration, so the detectors reject before they guess. - Local invariants, not global warps. The graph, seed, and validation pieces work on local neighbourhoods, so moderate perspective and radial distortion are handled without an explicit distortion model.
- Partial boards supported. PuzzleBoard gives absolute IDs from any visible fragment; ChArUco and marker boards label whatever is visible.
Tuning difficult cases
Most callers never need to tune. When defaults fail:
- Switch to
detect_*_bestwith the built-in 3-config sweep. - Inspect which config succeeded (or none) — the sweep logs counts.
- If all fail, open the corresponding detector README: chessboard, ChArUco, PuzzleBoard, marker, or the book tuning chapter for cross-detector guidance.
Limitations
- One target instance per image. Multiple simultaneous boards are not disambiguated; the largest detection wins.
- Pinhole-ish optics only. Moderate perspective and radial distortion are handled gracefully; fisheye and extreme wide-angle lenses are not supported.
- Grayscale input. Colour images must be converted by the caller
(
.to_luma8()). - No temporal tracking. Every call is independent.
- Roughly-square cells. Strongly anisotropic aspect ratios degrade detection — rescale the input first.
Printable targets
calib_targets::printable re-exports calib-targets-print.
PrintableTargetDocument is the canonical JSON input, and
write_target_bundle writes <stem>.json, <stem>.svg, <stem>.png
in one call. The calib_targets::generate module adds ergonomic
constructors (chessboard_document, charuco_document,
puzzleboard_document, marker_board_document) that hide the
TargetSpec enum wrapping. Ready-made specs live under
testdata/printable/.
CLI
cargo install calib-targets ships a calib-targets binary with two
generation flows:
# One-step: flags directly to JSON+SVG+PNG bundle
# Two-step: init a reviewable spec first, then render
Run calib-targets list-dictionaries to enumerate built-in ArUco
dictionaries. The CLI is gated on the default cli feature; library-only
consumers can disable it with default-features = false.
Canonical guide: printable-target book chapter.
Features
image(default) — enables thecalib_targets::detecthelpers that takeimage::GrayImageinputs and runchess-cornersinternally.tracing— gates tracing spans across the workspace crates.diagnostics(off) — forwards tocalib-targets-chessboard/diagnosticsand gatesdetect_chessboard_with_diagnostics(theDebugFramechannel) plus thediagnose_charuco,diagnose_puzzleboard, anddiagnose_marker_boardfree functions (and their_with_cornerscounterparts). The hotdetect_*paths build no trace when this is off.
Migrating to the current release
The detector structs and these free functions are now one symmetric surface.
CharucoDetector, PuzzleBoardDetector and MarkerBoardDetector gained
detect(image), which runs the ChESS corner pass from the params.chess field
they always carried but never read; their old corner-consuming detect is now
detect_with_corners, and detect_with_diagnostics is now
diagnose_with_corners, joined by diagnose(image). MarkerBoardDetector
returns Result<_, MarkerBoardDetectError> in place of Option. Successful
detections are unchanged, and every break is a compile error rather than a
silent behaviour change.
See the Migration Guide for before/after snippets covering Rust, the C ABI, Python and WebAssembly.
Examples
Other bindings
- Python —
calib-targets-pywraps the same facade viamaturin. The Python package name iscalib_targets. - WebAssembly —
@vitavision/calib-targetson npm exposes the detectors to the browser. - C FFI —
calib-targets-ffiexposes a stable C ABI with CMake package.
Crate map
| Re-export | Crate |
|---|---|
calib_targets::core |
calib-targets-core — shared types, homographies |
calib_targets::chessboard |
calib-targets-chessboard — invariant-first chessboard |
calib_targets::aruco |
calib-targets-aruco — ArUco / AprilTag dictionaries + decoding |
calib_targets::charuco |
calib-targets-charuco — ChArUco detection |
calib_targets::puzzleboard |
calib-targets-puzzleboard — self-identifying chessboard |
calib_targets::marker |
calib-targets-marker — checkerboard + 3 circle markers |
calib_targets::printable |
calib-targets-print — printable targets |
Underneath everything sits the standalone projective-grid library —
useful if you want grid construction without the calibration layer.