calib-targets-marker 0.9.0

Checkerboard marker target detector (checkerboard + 3 central circles)
Documentation
//! JSON configuration and report helpers for marker board detection.

use crate::{MarkerBoardDetectionResult, MarkerBoardDetector, MarkerBoardParams};
use calib_targets_chessboard::ChessCorner;
use calib_targets_core::io::{self, IoError};
use calib_targets_core::{DetectorConfig, GridAlignment, TargetDetection};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};

/// Error type for marker-board JSON config / report I/O.
pub type MarkerBoardIoError = IoError;

/// Configuration for marker board detection, loadable from JSON.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarkerBoardDetectConfig {
    /// Path to the input image to run detection on.
    pub image_path: String,
    /// Optional path for the detection report; defaults applied by
    /// [`MarkerBoardDetectConfig::output_path`] when unset.
    #[serde(default)]
    pub output_path: Option<String>,
    /// ChESS corner-detector configuration. Consumed by the upstream
    /// corner-detection step, not by the chessboard stage itself.
    #[serde(default)]
    pub chess: DetectorConfig,
    /// Marker-board detector parameters (layout + tuning).
    pub marker: MarkerBoardParams,
}

impl MarkerBoardDetectConfig {
    /// Load a JSON config from disk.
    pub fn load_json(path: impl AsRef<Path>) -> Result<Self, MarkerBoardIoError> {
        io::load_json(path)
    }

    /// Write this config to disk as pretty JSON.
    pub fn write_json(&self, path: impl AsRef<Path>) -> Result<(), MarkerBoardIoError> {
        io::write_json(self, path)
    }

    /// Resolve the output report path.
    pub fn output_path(&self) -> PathBuf {
        self.output_path
            .as_ref()
            .map(PathBuf::from)
            .unwrap_or_else(|| PathBuf::from("marker_board_detect_report.json"))
    }

    /// Build a detector from this config.
    ///
    /// Note: the chessboard detector (`DetectorParams`) does not include
    /// a nested ChESS detector config — `cfg.chess` is consumed upstream by
    /// the corner-detection step (`calib_targets::detect_corners`), not by
    /// the chessboard stage itself.
    pub fn build_detector(&self) -> MarkerBoardDetector {
        let params = self.marker.clone();
        MarkerBoardDetector::new(params)
    }
}

/// Detection report for serialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarkerBoardDetectReport {
    /// Path of the image detection was run on.
    pub image_path: String,
    /// Path of the JSON config that produced this report.
    pub config_path: String,
    /// Number of raw ChESS corners detected before board detection.
    pub num_raw_corners: usize,
    /// The raw ChESS corners detected in the image.
    pub raw_corners: Vec<ChessCorner>,
    /// The detected board, when detection succeeded.
    #[serde(default)]
    pub detection: Option<TargetDetection>,
    /// Grid alignment to the known board layout, when it could be resolved.
    #[serde(default)]
    pub alignment: Option<GridAlignment>,
    /// Human-readable error message, when detection failed.
    #[serde(default)]
    pub error: Option<String>,
}

impl MarkerBoardDetectReport {
    /// Build a base report from the input config and raw corners.
    pub fn new(
        cfg: &MarkerBoardDetectConfig,
        config_path: &Path,
        raw_corners: Vec<ChessCorner>,
    ) -> Self {
        Self {
            image_path: cfg.image_path.clone(),
            config_path: config_path.to_string_lossy().into_owned(),
            num_raw_corners: raw_corners.len(),
            raw_corners,
            detection: None,
            alignment: None,
            error: None,
        }
    }

    /// Populate report fields from a successful detection.
    pub fn set_detection(&mut self, res: MarkerBoardDetectionResult) {
        self.detection = Some(res.target_detection());
        self.alignment = res.alignment;
        self.error = None;
    }

    /// Load a report from JSON on disk.
    pub fn load_json(path: impl AsRef<Path>) -> Result<Self, MarkerBoardIoError> {
        io::load_json(path)
    }

    /// Write this report to disk as pretty JSON.
    pub fn write_json(&self, path: impl AsRef<Path>) -> Result<(), MarkerBoardIoError> {
        io::write_json(self, path)
    }
}