use serde::{Deserialize, Serialize};
use calib_targets_chessboard::{ChessboardParams, GridGraphParams};
use calib_targets_core::{GridAlignment, TargetDetection};
use crate::circle_score::{CircleCandidate, CirclePolarity, CircleScoreParams};
use crate::coords::{CellCoords, CellOffset};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct MarkerCircleSpec {
pub cell: CellCoords,
pub polarity: CirclePolarity,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MarkerBoardLayout {
pub rows: u32,
pub cols: u32,
#[serde(default)]
pub cell_size: Option<f32>,
pub circles: [MarkerCircleSpec; 3],
}
impl Default for MarkerBoardLayout {
fn default() -> Self {
Self {
rows: 6,
cols: 8,
cell_size: None,
circles: [
MarkerCircleSpec {
cell: CellCoords { i: 2, j: 2 },
polarity: CirclePolarity::White,
},
MarkerCircleSpec {
cell: CellCoords { i: 3, j: 2 },
polarity: CirclePolarity::Black,
},
MarkerCircleSpec {
cell: CellCoords { i: 2, j: 3 },
polarity: CirclePolarity::White,
},
],
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CircleMatchParams {
pub max_candidates_per_polarity: usize,
pub max_distance_cells: Option<f32>,
pub min_offset_inliers: usize,
}
impl Default for CircleMatchParams {
fn default() -> Self {
Self {
max_candidates_per_polarity: 6,
max_distance_cells: None,
min_offset_inliers: 1,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MarkerBoardParams {
pub layout: MarkerBoardLayout,
#[serde(default = "default_marker_chessboard_params")]
pub chessboard: ChessboardParams,
#[serde(default)]
pub grid_graph: GridGraphParams,
#[serde(default)]
pub circle_score: CircleScoreParams,
#[serde(default)]
pub match_params: CircleMatchParams,
#[serde(default)]
pub roi_cells: Option<[i32; 4]>,
}
impl MarkerBoardParams {
pub fn new(layout: MarkerBoardLayout) -> Self {
let mut chessboard = default_marker_chessboard_params();
chessboard.expected_rows = Some(layout.rows);
chessboard.expected_cols = Some(layout.cols);
Self {
layout,
chessboard,
grid_graph: GridGraphParams::default(),
circle_score: CircleScoreParams::default(),
match_params: CircleMatchParams::default(),
roi_cells: None,
}
}
}
impl Default for MarkerBoardParams {
fn default() -> Self {
Self::new(MarkerBoardLayout::default())
}
}
fn default_marker_chessboard_params() -> ChessboardParams {
ChessboardParams {
completeness_threshold: 0.05,
..ChessboardParams::default()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CircleMatch {
pub expected: MarkerCircleSpec,
pub matched_index: Option<usize>,
pub distance_cells: Option<f32>,
pub offset_cells: Option<CellOffset>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MarkerBoardDetectionResult {
pub detection: TargetDetection,
pub inliers: Vec<usize>,
pub circle_candidates: Vec<CircleCandidate>,
pub circle_matches: Vec<CircleMatch>,
pub alignment: Option<GridAlignment>,
pub alignment_inliers: usize,
}
impl MarkerBoardDetectionResult {
pub fn aligned_detection(&self) -> Option<TargetDetection> {
self.alignment.map(|_| self.detection.clone())
}
}