1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! Ergonomic ChESS / Radon corner-detector facade over
//! `chess-corners-core`.
//!
//! # Overview
//!
//! This crate is the high-level entry point for two chessboard-corner
//! detectors that share the same output surface:
//!
//! - **ChESS** (Chess-board Extraction by Subtraction and Summation)
//! — a dense ring-difference response with NMS and a pluggable
//! subpixel refiner. This is the default path and the fastest preset
//! in the repository's clean-image benchmark.
//! - **Radon** — a whole-image Duda-Frese accumulator that scores
//! corners by summing ray intensities through each pixel. It is useful
//! when the ChESS ring does not produce enough seeds, especially in
//! the small-cell, blur, and low-contrast fixtures covered by the
//! tests.
//!
//! The [`Detector`] struct ties together the active strategy, the
//! orientation fit, and the multiscale / upscale scratch buffers
//! behind a single `detect` call. It returns subpixel
//! [`CornerDescriptor`] values in full-resolution input coordinates.
//! In most applications you construct a [`DetectorConfig`] (typically
//! via [`DetectorConfig::chess`], [`DetectorConfig::chess_multiscale`],
//! [`DetectorConfig::radon`], or [`DetectorConfig::radon_multiscale`]),
//! optionally tweak its fields, build a [`Detector`], and call
//! [`Detector::detect`].
//!
//! Building a [`Detector`] once and calling [`Detector::detect`] in a
//! loop reuses the pyramid, response, and upscale scratch buffers
//! across frames — no per-frame allocation.
//!
//! # Quick start
//!
//! ## Using `image` (default)
//!
//! The default feature set includes integration with the `image`
//! crate. This example reads from disk and is marked `no_run`:
//!
//! ```no_run
//! use chess_corners::{ChessRefiner, Detector, DetectorConfig, Threshold};
//! use image::io::Reader as ImageReader;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let img = ImageReader::open("board.png")?
//! .decode()?
//! .to_luma8();
//!
//! let cfg = DetectorConfig::chess_multiscale()
//! .with_threshold(Threshold::Relative(0.15))
//! .with_chess(|c| c.refiner = ChessRefiner::forstner());
//!
//! let mut detector = Detector::new(cfg)?;
//! let corners = detector.detect(&img)?;
//! println!("found {} corners", corners.len());
//!
//! for c in &corners {
//! println!(
//! "corner at ({:.2}, {:.2}), response {:.1}, axes [{:.2}, {:.2}] rad",
//! c.x, c.y, c.response, c.axes[0].angle, c.axes[1].angle,
//! );
//! }
//! # Ok(()) }
//! ```
//!
//! ## Raw grayscale buffer
//!
//! If you already have an 8-bit grayscale buffer, call
//! [`Detector::detect_u8`]:
//!
//! ```
//! use chess_corners::{Detector, DetectorConfig};
//!
//! // 8×8 black/white checkerboard of 16-pixel squares (128×128).
//! let mut img = vec![0u8; 128 * 128];
//! for y in 0..128 {
//! for x in 0..128 {
//! if ((x / 16) + (y / 16)) % 2 == 0 {
//! img[y * 128 + x] = 255;
//! }
//! }
//! }
//!
//! let cfg = DetectorConfig::chess();
//! let mut detector = Detector::new(cfg)?;
//! let corners = detector.detect_u8(&img, 128, 128)?;
//! assert!(!corners.is_empty());
//! # Ok::<(), chess_corners::ChessError>(())
//! ```
//!
//! ## Radon strategy
//!
//! Switch to the whole-image Radon detector when ChESS misses corners on
//! the images you care about. The strategy lives inside
//! [`DetectorConfig::strategy`]; pick a Radon preset to get sensible
//! defaults:
//!
//! ```
//! use chess_corners::{Detector, DetectorConfig};
//!
//! let mut img = vec![0u8; 128 * 128];
//! for y in 0..128 {
//! for x in 0..128 {
//! if ((x / 16) + (y / 16)) % 2 == 0 {
//! img[y * 128 + x] = 255;
//! }
//! }
//! }
//!
//! let cfg = DetectorConfig::radon();
//! let mut detector = Detector::new(cfg)?;
//! let corners = detector.detect_u8(&img, 128, 128)?;
//! assert!(!corners.is_empty());
//! # Ok::<(), chess_corners::ChessError>(())
//! ```
//!
//! ## ML refiner (feature `ml-refiner`)
//!
//! Pick the ML pipeline by selecting `ChessRefiner::Ml` inside the
//! ChESS strategy. The example is marked `no_run` because loading the
//! embedded ONNX model on first use is not appropriate for a doctest:
//!
//! ```no_run
//! # #[cfg(feature = "ml-refiner")]
//! # {
//! use chess_corners::{ChessRefiner, Detector, DetectorConfig};
//! use image::GrayImage;
//!
//! let cfg = DetectorConfig::chess()
//! .with_chess(|c| c.refiner = ChessRefiner::Ml);
//!
//! let img: GrayImage = image::open("board.png").unwrap().to_luma8();
//! let mut detector = Detector::new(cfg).unwrap();
//! let corners = detector.detect(&img).unwrap();
//! # let _ = corners;
//! # }
//! ```
//!
//! The ML refiner runs a small ONNX model on normalized intensity
//! patches (uint8 / 255.0) centered at each candidate. The model
//! predicts `[dx, dy, conf_logit]`, but the confidence output is
//! currently ignored; the offsets are applied directly. Current
//! accuracy benchmarks are synthetic; real-world accuracy still needs
//! validation. Per-refiner cost is measured in Part VIII §7.6 of the
//! book. The ML path is slower than the hand-coded refiners and should
//! be chosen only after measuring that its behavior helps your data.
//!
//! ## Python and JavaScript bindings
//!
//! The workspace also ships bindings that wrap this facade:
//!
//! - `crates/chess-corners-py` (PyO3 / maturin) exposes a
//! `chess_corners.Detector` class whose `detect(image)` method
//! accepts a 2D `uint8` NumPy array and returns a `float32`
//! `(N, 9)` array with columns `[x, y, response, contrast, fit_rms,
//! axis0_angle, axis0_sigma, axis1_angle, axis1_sigma]`. See its
//! README for usage and configuration details.
//! - `crates/chess-corners-wasm` (wasm-bindgen / wasm-pack) exposes
//! the same surface to JavaScript / TypeScript via the
//! `@vitavision/chess-corners` npm package.
//!
//! # Configuration
//!
//! [`DetectorConfig`] is strategy-typed: the [`DetectorConfig::strategy`]
//! field is a [`DetectionStrategy`] enum carrying either a
//! [`ChessConfig`] (detector ring, descriptor ring, NMS, refiner) or
//! a [`RadonConfig`] (whole-image Duda-Frese parameters). Acceptance
//! is a single [`Threshold`] enum (`Absolute` or `Relative`).
//! [`MultiscaleConfig`] and [`UpscaleConfig`] live at the top level
//! and apply to both strategies. The detector translates this into
//! lower-level parameter structs internally; those structs
//! (`ChessParams`, `RadonDetectorParams`) are exposed for hand-composed
//! pipelines in [`low_level`].
//!
//! Two opt-in channels sit alongside the primary [`Detector`] API for
//! callers who need more than a finished detection result. The curated
//! low-level surface for hand-composing the detection pipeline
//! (response -> detect -> describe), including its parameter structs and
//! scratch buffers, lives in [`low_level`]; intermediate response maps
//! and Radon heatmaps for debugging and visualization live in
//! [`diagnostics`]. Both carry a weaker stability promise than the
//! facade root and are not needed by typical consumers. For deeper
//! internals (ring offsets, SAT views, scalar reference paths) depend on
//! `chess-corners-core` directly.
//!
//! # Features
//!
//! - `image` *(default)* – enables [`Detector::detect`] and
//! `image::GrayImage` integration.
//! - `rayon` – parallelizes response computation and multiscale
//! refinement over image rows. Combine with `par_pyramid` to
//! parallelize pyramid downsampling as well.
//! - `ml-refiner` – enables the ML-backed refiner entry points via the
//! `chess-corners-ml` crate and embedded ONNX model.
//! - `simd` – enables portable-SIMD accelerated inner loops for the
//! response kernel (requires a nightly compiler). Combine with
//! `par_pyramid` to SIMD-accelerate pyramid downsampling.
//! - `par_pyramid` – opt-in gate for SIMD/`rayon` acceleration inside
//! the pyramid builder.
//! - `tracing` – emits structured spans for multiscale detection,
//! suitable for use with `tracing-subscriber` or JSON tracing from
//! the CLI.
//! - `cli` – builds the `chess-corners` binary shipped with this
//! crate; it is not required when using the library as a
//! dependency.
//!
//! The library API is stable across feature combinations; features
//! only affect performance and observability, not numerical results.
//!
//! # References
//!
//! - Bennett, Lasenby. *ChESS: A Fast and Accurate Chessboard Corner
//! Detector*. CVIU 2014.
//! - Duda, Frese. *Accurate Detection and Localization of Checkerboard
//! Corners for Calibration*. BMVC 2018.
// The crate root surfaces only the stable facade: the detector, its
// configuration and result types, and errors. Diagnostic outputs are
// reachable via [`diagnostics`]; low-level pipeline stages, parameter
// structs, and scratch buffers via [`low_level`]; deeper internals
// (ring offsets, SAT views, scalar reference paths) via a direct
// `chess-corners-core` dependency.
pub use crate;
pub use crateChessError;
pub use crate;
pub use ;
// Primary detector entry point.
pub use crateDetector;