calib_targets_charuco/
io.rs1use crate::{
4 CharucoBoard, CharucoBoardError, CharucoBoardSpec, CharucoDetectError, CharucoDetectionResult,
5 CharucoDetector, CharucoDetectorParams,
6};
7use calib_targets_aruco::{ArucoScanConfig, MarkerDetection};
8use calib_targets_chessboard::{ChessboardParams, GridGraphParams};
9use calib_targets_core::{Corner, GridAlignment, TargetDetection};
10use serde::{Deserialize, Serialize};
11use std::{
12 fs,
13 path::{Path, PathBuf},
14};
15
16#[derive(thiserror::Error, Debug)]
17pub enum CharucoIoError {
18 #[error(transparent)]
19 Io(#[from] std::io::Error),
20 #[error(transparent)]
21 Json(#[from] serde_json::Error),
22}
23
24#[derive(thiserror::Error, Debug)]
25pub enum CharucoConfigError {
26 #[error(transparent)]
27 Board(#[from] CharucoBoardError),
28}
29
30fn default_px_per_square() -> f32 {
31 60.0
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct CharucoDetectConfig {
37 pub image_path: String,
38 pub board: CharucoBoardSpec,
39 #[serde(default)]
40 pub output_path: Option<String>,
41 #[serde(default)]
42 pub rectified_path: Option<String>,
43 #[serde(default)]
44 pub mesh_rectified_path: Option<String>,
45 #[serde(default = "default_px_per_square")]
46 pub px_per_square: f32,
47 #[serde(default)]
48 pub min_marker_inliers: Option<usize>,
49 #[serde(default)]
50 pub chessboard: Option<ChessboardParams>,
51 #[serde(default)]
52 pub graph: Option<GridGraphParams>,
53 #[serde(default)]
54 pub aruco: Option<ArucoScanConfig>,
55}
56
57impl CharucoDetectConfig {
58 pub fn load_json(path: impl AsRef<Path>) -> Result<Self, CharucoIoError> {
60 let raw = fs::read_to_string(path)?;
61 Ok(serde_json::from_str(&raw)?)
62 }
63
64 pub fn write_json(&self, path: impl AsRef<Path>) -> Result<(), CharucoIoError> {
66 let json = serde_json::to_string_pretty(self)?;
67 fs::write(path, json)?;
68 Ok(())
69 }
70
71 pub fn output_path(&self) -> PathBuf {
73 self.output_path
74 .as_ref()
75 .map(PathBuf::from)
76 .unwrap_or_else(|| PathBuf::from("charuco_detect_report.json"))
77 }
78
79 pub fn build_board(&self) -> Result<CharucoBoard, CharucoConfigError> {
81 Ok(CharucoBoard::new(self.board)?)
82 }
83
84 pub fn build_params(&self) -> CharucoDetectorParams {
86 let mut params = CharucoDetectorParams::for_board(&self.board);
87 params.px_per_square = self.px_per_square;
88 if let Some(min_marker_inliers) = self.min_marker_inliers {
89 params.min_marker_inliers = min_marker_inliers;
90 }
91 if let Some(chessboard) = self.chessboard.clone() {
92 params.chessboard = chessboard;
93 }
94 if let Some(graph) = self.graph.clone() {
95 params.graph = graph;
96 }
97 if let Some(aruco) = self.aruco.as_ref() {
98 if let Some(max_hamming) = aruco.max_hamming {
99 params.max_hamming = max_hamming;
100 }
101 aruco.apply_to_scan(&mut params.scan);
102 }
103 params
104 }
105
106 pub fn build_detector(&self) -> Result<CharucoDetector, CharucoConfigError> {
108 let params = self.build_params();
109 Ok(CharucoDetector::new(params)?)
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct CharucoDetectReport {
115 pub image_path: String,
116 pub config_path: String,
117 pub board: CharucoBoardSpec,
118 pub num_raw_corners: usize,
119 pub raw_corners: Vec<Corner>,
120 #[serde(default)]
121 pub detection: Option<TargetDetection>,
122 #[serde(default)]
123 pub markers: Option<Vec<MarkerDetection>>,
124 #[serde(default)]
125 pub alignment: Option<GridAlignment>,
126 #[serde(default)]
127 pub error: Option<String>,
128}
129
130impl CharucoDetectReport {
131 pub fn new(cfg: &CharucoDetectConfig, config_path: &Path, raw_corners: Vec<Corner>) -> Self {
133 Self {
134 image_path: cfg.image_path.clone(),
135 config_path: config_path.to_string_lossy().into_owned(),
136 board: cfg.board,
137 num_raw_corners: raw_corners.len(),
138 raw_corners,
139 detection: None,
140 markers: None,
141 alignment: None,
142 error: None,
143 }
144 }
145
146 pub fn set_detection(&mut self, res: CharucoDetectionResult) {
148 self.detection = Some(res.detection);
149 self.markers = Some(res.markers);
150 self.alignment = Some(res.alignment);
151 self.error = None;
152 }
153
154 pub fn set_error(&mut self, err: CharucoDetectError) {
156 self.error = Some(err.to_string());
157 }
158
159 pub fn load_json(path: impl AsRef<Path>) -> Result<Self, CharucoIoError> {
161 let raw = fs::read_to_string(path)?;
162 Ok(serde_json::from_str(&raw)?)
163 }
164
165 pub fn write_json(&self, path: impl AsRef<Path>) -> Result<(), CharucoIoError> {
167 let json = serde_json::to_string_pretty(self)?;
168 fs::write(path, json)?;
169 Ok(())
170 }
171}