calib-targets — Python bindings
Native-feeling Python API for the calib-targets Rust workspace.
Detects chessboards, ChArUco, PuzzleBoard, and marker boards, and
generates printable target bundles (JSON + SVG + PNG + DXF). Built with
PyO3 + maturin.
Python package name: calib_targets (the Rust crate is
calib-targets-py).
Install
# From source — this repo:
uv pip install maturin
uv run maturin develop --release -m crates/calib-targets-py/Cargo.toml
# Or from PyPI (pre-built wheels):
pip install calib-targets
Hello world
import numpy as np
from PIL import Image
import calib_targets as ct
image = np.asarray(Image.open("board.png").convert("L"), dtype=np.uint8)
result = ct.detect_chessboard_best(image, [ct.ChessboardParams()])
if result is not None:
print(f"labelled {len(result.corners)} corners")
End-to-end round-trip per target type
Each snippet covers: generate a printable target → load the PNG → detect → export detection to JSON.
Runnable scripts at
crates/calib-targets-py/examples/. Use any of
them as a starting point.
Chessboard
import io, json
import numpy as np
from PIL import Image
import calib_targets as ct
# 1. Generate target.
doc = ct.PrintableTargetDocument(
target=ct.ChessboardTargetSpec(inner_rows=7, inner_cols=9, square_size_mm=20.0),
page=ct.PageSpec(size=ct.PageSize.custom(width_mm=220.0, height_mm=180.0), margin_mm=10.0),
render=ct.RenderOptions(png_dpi=150),
)
bundle = ct.render_target_bundle(doc)
# 2. Load as grayscale numpy array.
image = np.asarray(Image.open(io.BytesIO(bundle.png_bytes)).convert("L"), dtype=np.uint8)
# 3. Detect — prefer *_best for robustness.
chess_cfg = ct.ChessConfig(threshold=15.0)
configs = [
ct.ChessboardParams(),
ct.ChessboardParams(min_labeled_corners=12),
ct.ChessboardParams(max_components=1),
]
result = ct.detect_chessboard_best(image, configs, chess_cfg=chess_cfg)
# 4. Export detection to JSON.
print(json.dumps(result.to_dict(), indent=2)[:200])
Runnable: examples/chessboard_roundtrip.py.
ChArUco
import calib_targets as ct
# (synthesise PNG as above; build matching board spec)
board = ct.CharucoBoardSpec(
rows=5, cols=7, cell_size=1.0, marker_size_rel=0.75,
dictionary="DICT_4X4_50", marker_layout=ct.MarkerLayout.OPENCV_CHARUCO,
)
params = ct.CharucoParams(
board=board, px_per_square=60.0,
chessboard=ct.ChessboardParams(),
min_marker_inliers=4,
)
result = ct.detect_charuco(image, params=params) # raises on failure
print(len(result.corners), "corners,", len(result.markers), "markers")
Runnable: examples/charuco_roundtrip.py.
Marker board
circles = (
ct.MarkerCircleSpec(i=3, j=2, polarity=ct.CirclePolarity.WHITE),
ct.MarkerCircleSpec(i=4, j=2, polarity=ct.CirclePolarity.BLACK),
ct.MarkerCircleSpec(i=4, j=3, polarity=ct.CirclePolarity.WHITE),
)
board = ct.MarkerBoardSpec(rows=6, cols=8, cell_size=1.0, circles=circles)
params = ct.MarkerBoardParams(board=board, chessboard=ct.ChessboardParams())
result = ct.detect_marker_board(image, params=params)
MarkerBoardSpec is also exported under its previous name MarkerBoardLayout
(a backward-compatible alias that stays live this release).
Runnable: examples/markerboard_roundtrip.py.
PuzzleBoard
params = ct.default_puzzleboard_params(rows=10, cols=10)
params.decode.search_mode = ct.PuzzleBoardSearchMode.fixed_board()
params.decode.scoring_mode = ct.PuzzleBoardScoringMode.soft_log_likelihood()
params.decode.symmetry_mode = ct.PuzzleBoardSymmetryMode.rotations() # default
result = ct.detect_puzzleboard(image, params=params)
# Every corner has an absolute master ID: result.corners[0].id
# Soft-mode scoring evidence is available from diagnose_puzzleboard().
Runnable: examples/puzzleboard_roundtrip.py.
Inputs
image: numpy.ndarray[uint8]with shape(h, w). Grayscale only; convert RGB upstream (Image.convert("L")).chess_cfg: ChessConfig | None— overrides the default ChESS corner detector.params: *Params— typed dataclass matching the detector. Dict inputs are rejected; use the typed classes.
Outputs
Every detection result is a typed dataclass with full attribute access,
editor autocomplete, and type stubs. Round-trip through JSON with
to_dict() and from_dict(...) — the dict schema matches the Rust
crate's serde_json output byte-for-byte.
payload = json.dumps(result.to_dict())
# ... later, elsewhere:
restored = ct.ChessboardDetectionResult.from_dict(json.loads(payload))
Every config / result type has these methods — ChessConfig,
ChessboardParams, CharucoParams, PuzzleBoardParams,
MarkerBoardParams, PrintableTargetDocument, and all result types.
Printable targets
One-liner helpers with sensible defaults (A4 portrait, 10 mm margins, 300 DPI):
doc = ct.charuco_document(rows=5, cols=7, square_size_mm=20.0,
marker_size_rel=0.75, dictionary="DICT_4X4_50")
written = ct.write_target_bundle(doc, "out/charuco_a4")
print(written.json_path, written.svg_path, written.png_path, written.dxf_path)
Other helpers: chessboard_document, puzzleboard_document,
marker_board_document. Each accepts optional page= / render=
overrides. For full control, construct PrintableTargetDocument
directly with one of the target specs (ChessboardTargetSpec,
CharucoTargetSpec, MarkerBoardTargetSpec, PuzzleBoardTargetSpec).
CLI
pip install calib-targets installs a calib-targets console script
that mirrors the Rust CLI:
calib-targets gen puzzleboard --rows 8 --cols 10 --square-size-mm 15 \
--out-stem puzzle
calib-targets list-dictionaries
calib-targets init chessboard --out spec.json \
--inner-rows 6 --inner-cols 8 --square-size-mm 20
calib-targets generate --spec spec.json --out-stem my_board
See testdata/printable/*.json for ready-made
spec files; every file is PrintableTargetDocument.from_dict( json.load(open(path)))-compatible.
Tuning difficult cases
- Replace
detect_*withdetect_*_bestand pass the matching sweep preset —ChessboardParams.sweep_default(),CharucoParams.sweep_for_board(board),MarkerBoardParams.sweep_for_board(board)orPuzzleBoardParams.sweep_for_board(board). Each preset is computed by Rust and handed to Python, so both language surfaces search the same configuration space. This is the recommended default. - Increase rasterisation / input resolution if cells are smaller than ~20 px across.
- Open the per-detector README for deeper guidance: chessboard, ChArUco, PuzzleBoard, marker. Python passes all parameters through to Rust, so tuning advice applies identically.
Limitations
- One target instance per image. Multiple simultaneous boards are not detected; pass cropped sub-images per target.
- Pinhole-ish optics only. Moderate radial / perspective distortion is handled gracefully; fisheye is not supported.
- Grayscale uint8 numpy arrays only. No torch tensors, no GPU.
- Board PNG / SVG generation for chessboard, ChArUco, marker board, and PuzzleBoard is supported; other target kinds are not.
Migration from pre-0.7 dict-based API
| Old | New |
|---|---|
detect_chessboard(img, params={"min_corner_strength": 0.5}) |
detect_chessboard(img, params=ChessboardParams(min_corner_strength=0.5)) |
detect_charuco(..., params={"board": {...}}) |
detect_charuco(..., params=CharucoParams(board=CharucoBoardSpec(...))) |
result["corners"] |
result.corners |
json.dumps(result_dict) |
json.dumps(result.to_dict()) |
Dict-based configuration is rejected in the new API; use the typed dataclasses.
ChessboardParams keeps the per-stage tuning knobs flat for ergonomics,
but to_dict() now nests everything except the four stable fields
(graph_build_algorithm, min_labeled_corners, max_components,
min_corner_strength) under an "advanced" block — matching the Rust
wire format. These advanced knobs are not covered by semver. The unused
projective_line_tol_rel knob was removed (it was a no-op); drop it from
any ChessboardParams(...) call that set it.
Feature parity vs Rust facade
detect_chessboard/_all/_best/_debug— ✔detect_charuco/_with_corners/_best,detect_puzzleboard/_with_corners/_best,detect_marker_board/_with_corners/_best— ✔diagnose_charuco/_with_corners,diagnose_puzzleboard/_with_corners,diagnose_marker_board/_with_corners— ✔- Printable targets for all four target kinds — ✔
to_dict/from_dictround-trip on every config + result type — ✔
Reusing a corner cloud across detectors
Each of ChArUco, PuzzleBoard, and marker board runs its own ChESS corner pass
from params.chess inside detect_*. The _with_corners variant skips that
pass and takes a corner cloud directly — useful when several detectors should
share one pass, or the corners come from a custom upstream:
corners = ct.trace_chessboard_topological(image, params=None)["corners"]
result = ct.detect_charuco_with_corners(image, corners, params=params)
corners is a list of ChessCorner-shaped dicts:
{"position": [x, y], "axes": [...], "strength": ...}.
Implementation note
The compiled Rust module is internal (calib_targets._core). Public
API stability is guaranteed only for top-level calib_targets exports.
Links
- Book (getting started + per-target chapters): https://vitalyvorobyev.github.io/calib-targets-rs/
- Rust facade crate: https://docs.rs/calib-targets
- Source + issue tracker: https://github.com/VitalyVorobyev/calib-targets-rs
Metadata
Release files for calib-targets 0.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| calib_targets-0.15.0.tar.gz | 818.9 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| calib_targets-0.15.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| calib_targets-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| calib_targets-0.15.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
Total release size: 5.0 MB
Release files / calib_targets-0.15.0.tar.gz
| Download URL | calib_targets-0.15.0.tar.gz |
|---|---|
| Size | 818.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3827ca3453f9ec8989d5e23dc13c1f70a092c72d49dcda9c6e2addc3aaaff62a
|
|
BLAKE2b-256 checksum How to use checksums |
d2fbbe63768bc8a1edfa2b7f4f21dfce8a240ae6754669fffa241b1b78a56609
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / calib_targets-0.15.0-cp310-abi3-win_amd64.whl
| Download URL | calib_targets-0.15.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
35e7f1e03c33ec4fb80cfdbd171d58fec059048cda29885fc23469b81d03ac98
|
|
BLAKE2b-256 checksum How to use checksums |
d9de8d1250339d9d5f990b27973fab4362e3b9e1dd0317bfc691f9ecbd5a1b0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / calib_targets-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | calib_targets-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.6 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
7857a76ffeee3ac27671ebbd4231c70e0874915ae41d026b0b1c98e8517cbe38
|
|
BLAKE2b-256 checksum How to use checksums |
a3e07b2d1e7b0673d412502715249c47f0d0a653168868f0889b6a1aeb7f7baf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / calib_targets-0.15.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | calib_targets-0.15.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.4 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
e4777e01b831deb0fcef6c4c74006295d25e7e7a3ac0c3af43dc7937bab2656f
|
|
BLAKE2b-256 checksum How to use checksums |
7a992bc3662d3cb8a8b6583b3458755db6509ec5489d2d2eb919c328bbdc2542
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency log