use serde::{Deserialize, Serialize};
use std::f32::consts::{FRAC_PI_2, FRAC_PI_4, PI};
pub const IMAGENET_MEAN: [f32; 3] = [0.485, 0.456, 0.406];
pub const IMAGENET_STD: [f32; 3] = [0.229, 0.224, 0.225];
pub const STRIDE_ALIGN: u32 = 32;
pub fn deg2rad(d: f32) -> f32 {
d * PI / 180.0
}
pub fn rad2deg(r: f32) -> f32 {
r * 180.0 / PI
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AngleDomain {
#[default]
Le90,
Le135,
OpenCv,
}
impl AngleDomain {
pub fn normalize(self, theta: f32) -> f32 {
match self {
Self::Le90 | Self::OpenCv => normalize_window(theta, -FRAC_PI_2),
Self::Le135 => normalize_window(theta, -FRAC_PI_4),
}
}
pub fn range(self) -> (f32, f32) {
match self {
Self::Le90 | Self::OpenCv => (-FRAC_PI_2, FRAC_PI_2),
Self::Le135 => (-FRAC_PI_4, 3.0 * FRAC_PI_4),
}
}
}
fn normalize_window(theta: f32, start: f32) -> f32 {
let mut t = (theta - start).rem_euclid(PI) + start;
if t - start >= PI {
t -= PI;
}
t
}
#[cfg(test)]
mod tests {
use super::*;
fn close(a: f32, b: f32) -> bool {
(a - b).abs() < 1e-5
}
#[test]
fn le90_wraps_quarter_turn() {
assert!(close(AngleDomain::Le90.normalize(FRAC_PI_2), -FRAC_PI_2));
assert!(close(AngleDomain::Le90.normalize(-PI), 0.0));
}
#[test]
fn le90_keeps_in_domain_values() {
for t in [0.0f32, -1.0, 1.0, -FRAC_PI_2] {
let n = AngleDomain::Le90.normalize(t);
assert!(close(n, t), "t={t} -> n={n}");
}
}
#[test]
fn normalize_respects_domain_window() {
for d in [AngleDomain::Le90, AngleDomain::Le135, AngleDomain::OpenCv] {
for t in [-3.0f32, -1.57, 0.0, 1.0, 2.5, 5.0, 10.0] {
let n = d.normalize(t);
let (lo, hi) = d.range();
assert!(n >= lo - 1e-5 && n < hi + 1e-5, "{d:?} t={t} -> {n}");
}
}
}
#[test]
fn radian_degree_roundtrip() {
assert!(close(rad2deg(deg2rad(37.5)), 37.5));
}
}