use anyhow::Context;
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum ImageInterpolation {
Nearest,
#[default]
Linear,
Cubic,
Area,
Lanczos4,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum ImageColorMode {
#[default]
Grayscale,
Rgb,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum PaddingColor {
Gray(u8),
Rgb([u8; 3]),
}
impl Default for PaddingColor {
fn default() -> Self {
PaddingColor::Rgb([114, 114, 114])
}
}
impl PaddingColor {
pub fn as_gray(&self) -> u8 {
match self {
PaddingColor::Gray(v) => *v,
PaddingColor::Rgb([r, _, _]) => *r,
}
}
pub fn as_rgb(&self) -> [u8; 3] {
match self {
PaddingColor::Gray(v) => [*v, *v, *v],
PaddingColor::Rgb(c) => *c,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct PlateConfig {
pub max_plate_slots: usize,
pub alphabet: String,
pub pad_char: char,
pub img_height: u32,
pub img_width: u32,
#[serde(default)]
pub keep_aspect_ratio: bool,
#[serde(default)]
pub interpolation: ImageInterpolation,
#[serde(default)]
pub image_color_mode: ImageColorMode,
#[serde(default)]
pub padding_color: PaddingColor,
#[serde(default)]
pub plate_regions: Option<Vec<String>>,
}
impl PlateConfig {
pub fn from_yaml(path: impl AsRef<Path>) -> anyhow::Result<Self> {
let text = std::fs::read_to_string(path.as_ref())
.with_context(|| format!("Cannot read config: {}", path.as_ref().display()))?;
let cfg: PlateConfig = serde_yml::from_str(&text)
.with_context(|| format!("Cannot parse config: {}", path.as_ref().display()))?;
Ok(cfg)
}
pub fn num_channels(&self) -> u32 {
match self.image_color_mode {
ImageColorMode::Rgb => 3,
ImageColorMode::Grayscale => 1,
}
}
pub fn has_region_recognition(&self) -> bool {
self.plate_regions
.as_ref()
.map_or(false, |v| !v.is_empty())
}
pub fn pad_idx(&self) -> usize {
self.alphabet
.chars()
.position(|c| c == self.pad_char)
.unwrap_or(0)
}
}