ChESS corner detection
Front-end crate:
chess-corners(external). This page documents the feature-input contract the workspace builds on — not an algorithm this workspace re-implements.
Every detector in the workspace starts from a cloud of ChESS X-junction corners — the sub-pixel saddle points where four chessboard squares meet. Corner finding itself is out of scope here: the workspace consumes a corner cloud and recovers structure from it. What matters downstream is the precise shape of each corner, because the axis clustering and topological grid stages read it directly.
The per-corner contract
Each detected corner carries:
- Sub-pixel position — image-frame pixel coordinates, origin top-left, x right, y down. Not rounded to integer pixels.
- Two undirected local axes —
axes: [AxisEstimate; 2], the two orthogonal grid directions visible in the corner’s immediate neighbourhood. EachAxisEstimateis anangle ∈ [0, π)plus a 1σ angular uncertainty (sigma). - A quality scalar —
strength, the ChESS response magnitude.
Together these are exactly what the prefilter reads. A corner’s axes are
admitted into grid construction when strength ≥ min_corner_strength
and neither axis is more uncertain than the axis-alignment tolerance
the grid builder will test it against
(max(σ₀, σ₁) ≤ topological.axis_align_tol_rad) — see
the chessboard pipeline. Before 0.11.0 the
second half of that rule was a fit-residual ratio
(fit_rms ≤ max_fit_rms_ratio · contrast); contrast and fit_rms no
longer exist upstream, and the σ-based rule is the direct replacement.
In the workspace’s shared types this corner is
calib_targets_core::AxisEstimate carried on a detector-specific input
type (e.g. calib_targets_chessboard::ChessCorner).
Orientation is axes-only
This is the load-bearing contract for everything downstream:
- There is no single-orientation field.
Corner::orientationwas removed workspace-wide and must never be reintroduced. The only orientation signal is the two-axis pair. - The two axes are undirected: an angle
θandθ + πdenote the same direction, so all axis comparisons work modulo π. - The axes are stored in fixed slots (
axes[0],axes[1]). The slot ordering encodes a local parity that adjacent chessboard corners flip — the topological grid finder and the chessboard wrapper’s parity discipline both depend on it. - A default-constructed / no-information axis carries
sigma = π(the no-info sentinel) and is filtered out before it can vote.
Circular means over axis angles must therefore accumulate
(cos 2θ, sin 2θ) and halve the resulting atan2. Doubling the angle
wraps θ and θ + π onto the same point, so the mean is stable across
the 0°/180° seam; naive (cos θ, sin θ) averaging collapses to zero
when votes straddle the wrap.
Orientation modes (DiskFit / RingFit)
The upstream detector exposes two axis-fitting modes, both still selectable through the facade / Studio / bench:
RingFitorders the two axis slots consistently by construction.DiskFitcan uniformly pick the wrong antipodal dark sector, reversing a corner’s(axes[0], axes[1])slot ordering relative to the board. The chessboard pipeline detects and repairs this globally in its axis-clustering stage (the slot-coherence repair), so aDiskFitswap never breaks the parity invariant.
Why the workspace stops at “corners in”
Corner finding is image processing; lattice recovery is geometry. Keeping the boundary here lets the geometry crates stay image-free and reusable: anything that can supply oriented point features — a different corner detector, a blob detector with a local-orientation estimate, a laser-dot extractor — can drive the same grid recovery. See The Grid Model for the generic feature shapes.
Cross-references
- Axis clustering — the first consumer of the dual-axis signal.
- The Grid Model — the generic
OrientedFeatureshapes ChESS corners are adapted into. - Conventions — the coordinate / orientation conventions in full.