glint_mask_tools/core/
algorithm.rs1use crate::error::{GlintError, Result};
7use ndarray::{Array2, Array3};
8
9pub trait GlintAlgorithm: Send + Sync {
15 fn detect_glint(&self, image: &Array3<f64>) -> Result<Array2<u8>>;
29
30 fn name(&self) -> &'static str;
32
33 fn description(&self) -> &'static str;
35
36 fn validate_parameters(&self) -> Result<()> {
38 Ok(())
40 }
41
42 fn required_bands(&self) -> Option<usize> {
46 None
47 }
48
49 fn supports_bands(&self, band_count: usize) -> bool {
51 match self.required_bands() {
52 Some(required) => band_count == required,
53 None => true,
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct AlgorithmConfig {
61 pub parameters: std::collections::HashMap<String, AlgorithmParameter>,
63}
64
65#[derive(Debug, Clone)]
67pub enum AlgorithmParameter {
68 Float(f64),
69 Int(i64),
70 Bool(bool),
71 String(String),
72 FloatArray(Vec<f64>),
73}
74
75impl AlgorithmParameter {
76 pub fn as_float(&self) -> Result<f64> {
78 match self {
79 AlgorithmParameter::Float(v) => Ok(*v),
80 _ => Err(GlintError::validation("Parameter is not a float")),
81 }
82 }
83
84 pub fn as_int(&self) -> Result<i64> {
86 match self {
87 AlgorithmParameter::Int(v) => Ok(*v),
88 _ => Err(GlintError::validation("Parameter is not an integer")),
89 }
90 }
91
92 pub fn as_bool(&self) -> Result<bool> {
94 match self {
95 AlgorithmParameter::Bool(v) => Ok(*v),
96 _ => Err(GlintError::validation("Parameter is not a boolean")),
97 }
98 }
99
100 pub fn as_string(&self) -> Result<&str> {
102 match self {
103 AlgorithmParameter::String(v) => Ok(v),
104 _ => Err(GlintError::validation("Parameter is not a string")),
105 }
106 }
107
108 pub fn as_float_array(&self) -> Result<&[f64]> {
110 match self {
111 AlgorithmParameter::FloatArray(v) => Ok(v),
112 _ => Err(GlintError::validation("Parameter is not a float array")),
113 }
114 }
115}
116
117impl From<f64> for AlgorithmParameter {
118 fn from(value: f64) -> Self {
119 AlgorithmParameter::Float(value)
120 }
121}
122
123impl From<i64> for AlgorithmParameter {
124 fn from(value: i64) -> Self {
125 AlgorithmParameter::Int(value)
126 }
127}
128
129impl From<bool> for AlgorithmParameter {
130 fn from(value: bool) -> Self {
131 AlgorithmParameter::Bool(value)
132 }
133}
134
135impl From<String> for AlgorithmParameter {
136 fn from(value: String) -> Self {
137 AlgorithmParameter::String(value)
138 }
139}
140
141impl From<Vec<f64>> for AlgorithmParameter {
142 fn from(value: Vec<f64>) -> Self {
143 AlgorithmParameter::FloatArray(value)
144 }
145}
146
147pub fn validate_threshold(threshold: f64) -> Result<()> {
149 if !(0.0..=1.0).contains(&threshold) {
150 Err(GlintError::InvalidThreshold { value: threshold })
151 } else {
152 Ok(())
153 }
154}
155
156pub fn validate_thresholds(thresholds: &[f64]) -> Result<()> {
158 for &threshold in thresholds {
159 validate_threshold(threshold)?;
160 }
161 Ok(())
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_algorithm_parameter_conversion() {
170 let param = AlgorithmParameter::from(0.5);
171 assert_eq!(param.as_float().unwrap(), 0.5);
172
173 let param = AlgorithmParameter::from(42i64);
174 assert_eq!(param.as_int().unwrap(), 42);
175
176 let param = AlgorithmParameter::from(true);
177 assert!(param.as_bool().unwrap());
178
179 let param = AlgorithmParameter::from("test".to_string());
180 assert_eq!(param.as_string().unwrap(), "test");
181
182 let param = AlgorithmParameter::from(vec![0.1, 0.2, 0.3]);
183 assert_eq!(param.as_float_array().unwrap(), &[0.1, 0.2, 0.3]);
184 }
185
186 #[test]
187 fn test_validate_threshold() {
188 assert!(validate_threshold(0.5).is_ok());
189 assert!(validate_threshold(0.0).is_ok());
190 assert!(validate_threshold(1.0).is_ok());
191 assert!(validate_threshold(-0.1).is_err());
192 assert!(validate_threshold(1.1).is_err());
193 }
194
195 #[test]
196 fn test_validate_thresholds() {
197 assert!(validate_thresholds(&[0.1, 0.5, 0.9]).is_ok());
198 assert!(validate_thresholds(&[0.1, 1.5, 0.9]).is_err());
199 }
200}