ChESS Corners | VitaVision
← Back to algorithms

ChESS Corners

Vitaly Vorobyev··4 min read
intermediate
computer-visionfeature-detectioncalibrationchessboard

Goal

Detect chessboard X-junctions in a grayscale image. Input: an image I:ΩRI: \Omega \to \mathbb{R} on pixel domain ΩZ2\Omega \subset \mathbb{Z}^2. Output: a set of integer pixel locations {(xi,yi)}Ω\{(x_i, y_i)\} \subset \Omega at which the local neighborhood matches an alternating bright-dark quadrant pattern. The detector is specific to X-junctions and rejects generic corners, straight edges, and narrow stripes.

Algorithm

Sampling is done at fixed integer offsets — no interpolation. Around a candidate pixel (x,y)(x, y), 16 ring samples I0,,I15I_0, \ldots, I_{15} are read at the offsets

Δ=[(0,5),(2,5),(3,3),(5,2),(5,0),(5,2),(3,3),(2,5),(0,5),(2,5),(3,3),(5,2),(5,0),(5,2),(3,3),(2,5)],\begin{aligned} \Delta = \big[\,&(0,-5),\,(2,-5),\,(3,-3),\,(5,-2),\,(5,0),\,(5,2),\,(3,3),\,(2,5), \\ &(0,5),\,(-2,5),\,(-3,3),\,(-5,2),\,(-5,0),\,(-5,-2),\,(-3,-3),\,(-2,-5)\,\big], \end{aligned}

arranged cyclically around the ring so that consecutive indices are adjacent on the ring and index n+8n+8 is opposite nn. The offsets are the FAST-16 pattern scaled to radius 5. The angular spacing alternates between 21.8° and 23.2°, close to the ideal 22.5°. Indices are taken modulo 16.

Two local means anchor the response. The local mean μ\mu_\ell averages the 5-pixel cross at the candidate:

μ=15(I(x,y)+I(x ⁣ ⁣1,y)+I(x ⁣+ ⁣1,y)+I(x,y ⁣ ⁣1)+I(x,y ⁣+ ⁣1)).\mu_\ell = \tfrac{1}{5}\bigl(I(x, y) + I(x\!-\!1, y) + I(x\!+\!1, y) + I(x, y\!-\!1) + I(x, y\!+\!1)\bigr).

The neighbour mean μn\mu_n averages the 16 ring samples:

μn=116k=015Ik.\mu_n = \tfrac{1}{16} \sum_{k=0}^{15} I_k.

Three per-pixel responses follow.

Definition
Sum response (SR)

Large when the ring shows the two-cycle alternation of a true X-junction.

SR=n=03(In+In+8)(In+4+In+12).\mathrm{SR} = \sum_{n=0}^{3} \bigl|\,(I_n + I_{n+8}) - (I_{n+4} + I_{n+12})\,\bigr|.
Definition
Diff response (DR)

Large on straight edges, where opposite samples disagree.

DR=n=07InIn+8.\mathrm{DR} = \sum_{n=0}^{7} |\,I_n - I_{n+8}\,|.
Definition
Mean response (MR)

Separates X-junctions from narrow stripes by comparing the two means.

MR=μnμ.\mathrm{MR} = |\,\mu_n - \mu_\ell\,|.
Definition
ChESS response (R)

The detector score. The factor 16 zeros out the degenerate case where only InI_n and In+8I_{n+8} are bright — a narrow stripe — making R0R \leq 0 there.

R=SRDR16MR.R = \mathrm{SR} - \mathrm{DR} - 16 \cdot \mathrm{MR}.

Procedure

Algorithm
ChESS corner detection
Input: Grayscale image II on domain Ω\Omega; offset table Δ\Delta; border margin m=5m = 5.
Output: Set of integer pixel locations {(xi,yi)}\{(x_i, y_i)\} marking detected X-junctions.
  1. For every pixel (x,y)Ω(x, y) \in \Omega at distance m\geq m from the image border, compute R(x,y)R(x, y) from the 16 ring samples and the 5-pixel cross.
  2. Positive threshold. Discard pixels with R(x,y)0R(x, y) \leq 0.
  3. Non-maximum suppression. In a small neighborhood (typically 3×33 \times 3 or 5×55 \times 5), keep only local maxima of RR.
  4. Response connectivity. Discard isolated positive-response pixels; a true X-junction produces a connected cluster.
  5. Neighbourhood comparison. Suppress maxima whose magnitude is small relative to nearby maxima, using an application-specific threshold.

Subpixel localization, orientation recovery, and multiscale detection via a Gaussian pyramid are standard extensions, not part of the base detector.

Implementation

The per-pixel response in Rust, given a row-major image:

const RING5: [(i32, i32); 16] = [
    (0, -5), (2, -5), (3, -3), (5, -2), (5, 0), (5, 2), (3, 3), (2, 5),
    (0, 5), (-2, 5), (-3, 3), (-5, 2), (-5, 0), (-5, -2), (-3, -3), (-2, -5),
];

fn chess_response(img: &[u8], w: usize, x: i32, y: i32) -> f32 {
    let mut s = [0i32; 16];
    for k in 0..16 {
        let (dx, dy) = RING5[k];
        s[k] = img[((y + dy) as usize) * w + (x + dx) as usize] as i32;
    }

    let mut sr = 0i32;
    for k in 0..4 {
        sr += ((s[k] + s[k + 8]) - (s[k + 4] + s[k + 12])).abs();
    }
    let mut dr = 0i32;
    for k in 0..8 {
        dr += (s[k] - s[k + 8]).abs();
    }

    let mu_n = s.iter().sum::<i32>() as f32 / 16.0;
    let c = |dx: i32, dy: i32| img[((y + dy) as usize) * w + (x + dx) as usize] as f32;
    let mu_l = (c(0, 0) + c(-1, 0) + c(1, 0) + c(0, -1) + c(0, 1)) / 5.0;

    (sr - dr) as f32 - 16.0 * (mu_n - mu_l).abs()
}

The 16 ring accesses and 5 cross accesses are fixed offsets, so the hot loop compiles to straight-line integer code with no branches and no interpolation. The full response map is computed by running chess_response over every pixel at distance 5\geq 5 from the border; on wide images the outer loop vectorizes cleanly over rows.

Remarks

  • Complexity: O(Ω)O(|\Omega|) per scale — 16 ring reads, 5 cross reads, and a fixed number of integer additions per pixel. No trigonometry, no interpolation.
  • Selective for chessboard-like X-junctions; not a general-purpose corner detector.
  • Ring radius is fixed to 5 pixels in the canonical design. For heavy blur or low-resolution targets, the paper also defines a radius-10 ring with the same angular pattern.
  • Orientation is ambiguous from ring samples alone — the response is symmetric under rotation by 9090^\circ. Orientation is recovered post-detection by maximizing the signed measure Mn=(In+In+8)(In+4+In+12)M_n = (I_n + I_{n+8}) - (I_{n+4} + I_{n+12}) over the eight discrete directions.

References

  1. S. Bennett, J. Lasenby. ChESS — Quick and Robust Detection of Chess-board Features. arXiv
    .5491, 2013. arxiv.org/abs/1301.5491
  2. E. Rosten, T. Drummond. Machine Learning for High-Speed Corner Detection. ECCV, 2006. — origin of the FAST-16 sampling pattern adopted for the ring.

Related Posts

Related Demos

Related Algorithms