# chess-corners

Ergonomic ChESS (Chess-board Extraction by Subtraction and Summation) detector
on top of `chess-corners-core`.

This crate is the public Rust API:

- flat `ChessConfig` with explicit semantic modes, threshold mode, and refiner selection
- single-scale and multiscale detection entry points
- optional `image::GrayImage` helpers
- optional CLI binary and ML-backed refinement entry points

`chess-corners-core` and `box-image-pyramid` remain available as lower-level
sharp tools, but `chess-corners` is the intended compatibility boundary.

## Quick start

```rust
use chess_corners::{ChessConfig, RefinementMethod, find_chess_corners_image};
use image::ImageReader;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let img = ImageReader::open("board.png")?.decode()?.to_luma8();

    let mut cfg = ChessConfig::multiscale();
    cfg.threshold_value = 0.15;
    cfg.refiner.kind = RefinementMethod::Forstner;

    let corners = find_chess_corners_image(&img, &cfg);
    println!("found {} corners", corners.len());
    Ok(())
}
```

## Public config shape

`ChessConfig` is intentionally flat:

```rust
use chess_corners::{
    ChessConfig, DescriptorMode, DetectorMode, RefinementMethod, ThresholdMode,
};

let mut cfg = ChessConfig::single_scale();
cfg.detector_mode = DetectorMode::Canonical;
cfg.descriptor_mode = DescriptorMode::FollowDetector;
cfg.threshold_mode = ThresholdMode::Relative;
cfg.threshold_value = 0.2;
cfg.nms_radius = 2;
cfg.min_cluster_size = 2;
cfg.pyramid_levels = 1;
cfg.pyramid_min_size = 128;
cfg.refinement_radius = 3;
cfg.merge_radius = 3.0;
cfg.refiner.kind = RefinementMethod::CenterOfMass;
```

Use `ChessConfig::single_scale()` for the default one-level detector and
`ChessConfig::multiscale()` for the recommended 3-level preset.

`DetectorMode::Broad` enables the wider, blur-tolerant detector response mode.
`DescriptorMode` can either follow the detector or override descriptor and
orientation sampling explicitly.

## Refiner configuration

`cfg.refiner` always contains all supported leaf configs:

- `cfg.refiner.center_of_mass`
- `cfg.refiner.forstner`
- `cfg.refiner.saddle_point`

Only `cfg.refiner.kind` selects which one is active:

```rust
use chess_corners::{ChessConfig, RefinementMethod};

let mut cfg = ChessConfig::single_scale();
cfg.refiner.kind = RefinementMethod::Forstner;
cfg.refiner.forstner.max_offset = 2.0;
```

You can also bypass the configured refiner for a single call with
`find_chess_corners_image_with_refiner` or `find_chess_corners_with_refiner`.

## CLI config shape

The CLI uses the same flat algorithm schema at the top level, combined with
application fields such as `image`, `output_json`, `output_png`, `log_level`,
and `ml`.

See:

- `config/chess_algorithm_config_example.json` for the shared algorithm config
- `config/chess_cli_config_example.json` for a complete CLI input

## ML refiner

Enable the `ml-refiner` feature to use the separate ML-backed pipeline:

```rust
use chess_corners::{ChessConfig, find_chess_corners_image_with_ml};
use image::GrayImage;

let img = GrayImage::new(1, 1);
let cfg = ChessConfig::single_scale();
let corners = find_chess_corners_image_with_ml(&img, &cfg);
```

The ML path is slower than the classic refiners and intentionally stays outside
the canonical `ChessConfig` schema.

## Examples

- Single-scale: `cargo run -p chess-corners --example single_scale_image -- testimages/mid.png`
- Multiscale: `cargo run -p chess-corners --example multiscale_image -- testimages/large.png`

## Feature flags

- `image` (default): `image::GrayImage` integration
- `rayon`: parallel response/refinement
- `simd`: portable-SIMD acceleration in the core response path
- `par_pyramid`: SIMD/`rayon` in pyramid construction
- `tracing`: structured spans
- `ml-refiner`: ONNX-backed ML refinement
- `cli`: build the `chess-corners` binary
