checkerboard_calibrate/chessboard/
detect.rs1use super::binarize::{adaptive_threshold_mean, equalize_hist};
17use super::board::extract_board;
18use super::contour::find_contours;
19use super::link::{connected_components, link_quads};
20use super::order::{assign_grid, order_all_corners};
21use super::quad::{Quad, contour_area, find_quads};
22
23const MAX_DILATIONS: usize = 7;
25
26fn dilate3x3(src: &[u8], w: usize, h: usize) -> Vec<u8> {
29 let mut dst = vec![0u8; src.len()];
30 for y in 0..h {
31 for x in 0..w {
32 let mut m = 0u8;
33 for dy in -1i32..=1 {
34 for dx in -1i32..=1 {
35 let nx = x as i32 + dx;
36 let ny = y as i32 + dy;
37 if nx >= 0 && ny >= 0 && (nx as usize) < w && (ny as usize) < h {
38 m = m.max(src[ny as usize * w + nx as usize]);
39 }
40 }
41 }
42 dst[y * w + x] = m;
43 }
44 }
45 dst
46}
47
48fn draw_border(img: &mut [u8], w: usize, h: usize, value: u8, thickness: usize) {
51 for y in 0..h {
52 for x in 0..w {
53 if x < thickness || y < thickness || x + thickness >= w || y + thickness >= h {
54 img[y * w + x] = value;
55 }
56 }
57 }
58}
59
60pub fn find_chessboard_corners(
65 gray: &[u8],
66 w: usize,
67 h: usize,
68 pattern_w: usize,
69 pattern_h: usize,
70) -> Option<Vec<(f32, f32)>> {
71 assert_eq!(gray.len(), w * h);
72 let eq = equalize_hist(gray);
73
74 let smaller = w.min(h);
78 let block_sizes: Vec<usize> = [smaller / 5, smaller / 9, smaller / 15]
79 .iter()
80 .map(|b| (b | 1).max(3))
81 .collect();
82 let min_area = 25.0;
84 let max_area = (w as f64) * (h as f64) * 0.5;
85
86 for dilations in 0..=MAX_DILATIONS {
87 for &block_size in &block_sizes {
88 for &delta in &[0.0f64, 5.0, 9.0] {
89 let mut bin = adaptive_threshold_mean(&eq, w, h, block_size, delta);
90 draw_border(&mut bin, w, h, 255, 1);
91 for _ in 0..dilations {
92 bin = dilate3x3(&bin, w, h);
93 }
94
95 let contours = find_contours(&bin, w, h);
96 let all_quads = find_quads(&contours, min_area);
97 let quads: Vec<Quad> = all_quads
98 .into_iter()
99 .filter(|q| {
100 let corners = [q.corners[0], q.corners[1], q.corners[2], q.corners[3]];
101 contour_area(&corners) <= max_area
102 })
103 .collect();
104 if quads.len() < pattern_w * pattern_h / 4 {
105 continue;
106 }
107
108 let mut linked = link_quads(&quads);
109 order_all_corners(&mut linked);
110 for comp in connected_components(&linked) {
111 let grid = assign_grid(&linked, &comp);
112 if let Some(corners) = extract_board(&linked, &grid, pattern_w, pattern_h) {
113 return Some(corners);
114 }
115 if let Some(corners) = extract_board(&linked, &grid, pattern_h, pattern_w) {
116 return Some(corners);
117 }
118 }
119 }
120 }
121 }
122 None
123}