Goal
Detect corners in a grayscale image on pixel domain . Output: a set of integer pixel locations . Instead of computing a continuous response from gradients, FAST applies a binary segment test on a 16-pixel Bresenham ring (the integer-pixel discretisation of a circle produced by Bresenham's midpoint circle algorithm) of radius 3: an arc of contiguous ring pixels must all be brighter than or all darker than . The test requires only integer comparisons; the high-speed cardinal-point early rejection reads only four ring pixels to discard the majority of candidates before the full ring is examined.
Algorithm
Symbols: — centre pixel intensity at candidate ; ring samples at the 16 offsets of the Bresenham circle of radius 3; — integer intensity threshold; — required arc length (segment-test parameter).
The 16 ring offsets, indexed clockwise from the top (one-based, matching Figure 1 of the paper):
Each ring sample is , with indices taken cyclically modulo 16.
For each ring sample relative to centre with threshold :
is a corner if there exists a contiguous arc of length on the cyclic ring where every has the same non-zero sign: consecutive ring pixels are all brighter than , or consecutive ring pixels are all darker than . Common values are (FAST-9) and (FAST-12). FAST-9 has higher repeatability under noise; FAST-12 admits a more aggressive cardinal-point early rejection.
The four ring pixels at one-based indices 1, 5, 9, 13 (top, right, bottom, left) are read first. The minimum number of cardinals contained in any -arc on the 16-cycle is
which evaluates to and . For a candidate to satisfy the segment-test criterion, at least of the four cardinals must be all-brighter-by- or all-darker-by-. If fewer qualify, is rejected without reading the remaining 12 ring pixels. Using the wrong (for example, requiring on FAST-9) causes false rejections.
The maximum threshold for which still passes the segment test, used for non-maximum suppression. Computed in closed form as:
where and .
Procedure
- For each pixel at distance from the image border:
- Apply the cardinal-point test on one-based indices 1, 5, 9, 13 (zero-based: 0, 4, 8, 12). If fewer than of these four are all-brighter-by- or all-darker-by-, reject (, ).
- Read all 16 ring samples and compute .
- Test for contiguous same-sign non-zero values on the cyclic ring. If found, mark as a corner candidate.
- Compute the corner score for each candidate via binary search on or the closed-form sum in equation (8) of the paper.
- Apply non-maximum suppression on over a neighbourhood; retain only local maxima.
Implementation
The per-pixel segment test in Rust:
const RING16: [(i32, i32); 16] = [
(0, -3), (1, -3), (2, -2), (3, -1), (3, 0), (3, 1), (2, 2), (1, 3),
(0, 3), (-1, 3), (-2, 2), (-3, 1), (-3, 0), (-3, -1), (-2, -2), (-1, -3),
];
fn is_fast_corner(img: &[u8], w: usize, x: i32, y: i32, t: i32, n: usize) -> bool {
let centre = img[(y as usize) * w + x as usize] as i32;
// Min cardinals in any N-arc on the 16-cycle: 3 for N>=12, 2 for N>=9, 1 otherwise.
let card_min = if n >= 12 { 3 } else if n >= 9 { 2 } else { 1 };
let mut bright_card = 0;
let mut dark_card = 0;
for &k in &[0usize, 4, 8, 12] {
let (dx, dy) = RING16[k];
let v = img[((y + dy) as usize) * w + (x + dx) as usize] as i32;
if v > centre + t { bright_card += 1; }
else if v < centre - t { dark_card += 1; }
}
if bright_card < card_min && dark_card < card_min { return false; }
// Full segment test on the cyclic ring.
let mut s = [0i8; 16];
for k in 0..16 {
let (dx, dy) = RING16[k];
let v = img[((y + dy) as usize) * w + (x + dx) as usize] as i32;
s[k] = if v > centre + t { 1 } else if v < centre - t { -1 } else { 0 };
}
let mut run = 0usize;
let mut sign = 0i8;
for k in 0..16 + n - 1 {
let v = s[k % 16];
if v != 0 && v == sign { run += 1; if run >= n { return true; } }
else { sign = v; run = if v != 0 { 1 } else { 0 }; }
}
false
}
The cardinal-point check uses zero-based indices 0, 4, 8, 12 (one-based 1, 5, 9, 13 in the paper). The threshold card_min is the minimum number of cardinals contained in any -arc on the 16-cycle; using a larger value would falsely reject corners. The cyclic wrap is handled by iterating up to 16 + n - 1 and indexing modulo 16, which correctly detects arcs straddling the index-15 / index-0 boundary.
Remarks
- Complexity: per scale. The per-pixel cost is dominated by the cardinal-point test for non-corners (4 reads and 8 comparisons) and the full segment test for candidates (16 reads). The inner loop vectorizes over rows.
- Choice of : FAST-9 has the highest repeatability of the FAST family (Figure 6A of the paper). FAST-12 is faster per pixel because the cardinal-point test rejects more aggressively, but produces fewer detections under noise.
- Threshold : a fixed integer offset in pixel intensity units. The detector's recall–precision tradeoff is controlled directly by ; no implicit sensitivity parameter exists.
- Limitation: the segment-test criterion produces no continuous gradient response. Non-maximum suppression relies on the score from equation (8), which adds a constant per-corner cost.
- Limitation: not rotation-invariant in the strict sense — the discrete ring breaks rotation symmetry. The detector also responds to one-pixel-wide lines at certain angles where the quantised circle misses the line.
- The 16-pixel ring offset table is reused by the ChESS corner detector at a scaled radius of 5 pixels (see related algorithms).
References
- E. Rosten, T. Drummond. Machine Learning for High-Speed Corner Detection. ECCV, 2006. URL: edwardrosten.com/work/rosten_2006_machine.pdf