Skip to main content

codec_eval/corpus/
category.rs

1//! Image category classification.
2
3use serde::{Deserialize, Serialize};
4
5/// Category of an image for per-category analysis.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum ImageCategory {
9    /// Photographic content.
10    Photo,
11    /// Digital illustrations, drawings, artwork.
12    Illustration,
13    /// Text-heavy images, documents.
14    Text,
15    /// Screenshots, UI captures.
16    Screenshot,
17    /// High-frequency detail (textures, foliage).
18    HighFrequency,
19    /// Low-frequency content (sky, gradients).
20    LowFrequency,
21    /// Smooth gradients.
22    Gradient,
23    /// Repeating patterns.
24    Pattern,
25    /// Computer-generated imagery.
26    Cgi,
27    /// Medical or scientific imagery.
28    Scientific,
29    /// Uncategorized.
30    Other,
31}
32
33impl ImageCategory {
34    /// Get all category variants.
35    #[must_use]
36    pub fn all() -> &'static [Self] {
37        &[
38            Self::Photo,
39            Self::Illustration,
40            Self::Text,
41            Self::Screenshot,
42            Self::HighFrequency,
43            Self::LowFrequency,
44            Self::Gradient,
45            Self::Pattern,
46            Self::Cgi,
47            Self::Scientific,
48            Self::Other,
49        ]
50    }
51
52    /// Parse from string (case-insensitive).
53    #[must_use]
54    pub fn from_str_loose(s: &str) -> Option<Self> {
55        match s.to_lowercase().as_str() {
56            "photo" | "photograph" | "photos" => Some(Self::Photo),
57            "illustration" | "drawing" | "art" | "artwork" => Some(Self::Illustration),
58            "text" | "document" | "docs" => Some(Self::Text),
59            "screenshot" | "screenshots" | "ui" => Some(Self::Screenshot),
60            "high_frequency" | "highfreq" | "texture" | "textures" => Some(Self::HighFrequency),
61            "low_frequency" | "lowfreq" | "smooth" => Some(Self::LowFrequency),
62            "gradient" | "gradients" => Some(Self::Gradient),
63            "pattern" | "patterns" => Some(Self::Pattern),
64            "cgi" | "render" | "3d" => Some(Self::Cgi),
65            "scientific" | "medical" | "science" => Some(Self::Scientific),
66            "other" | "misc" | "unknown" => Some(Self::Other),
67            _ => None,
68        }
69    }
70
71    /// Get a description of this category.
72    #[must_use]
73    pub fn description(self) -> &'static str {
74        match self {
75            Self::Photo => "Photographic content",
76            Self::Illustration => "Digital illustrations and artwork",
77            Self::Text => "Text-heavy images and documents",
78            Self::Screenshot => "Screenshots and UI captures",
79            Self::HighFrequency => "High-frequency detail (textures, foliage)",
80            Self::LowFrequency => "Low-frequency content (sky, gradients)",
81            Self::Gradient => "Smooth gradients",
82            Self::Pattern => "Repeating patterns",
83            Self::Cgi => "Computer-generated imagery",
84            Self::Scientific => "Medical or scientific imagery",
85            Self::Other => "Uncategorized",
86        }
87    }
88}
89
90impl std::fmt::Display for ImageCategory {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            Self::Photo => write!(f, "photo"),
94            Self::Illustration => write!(f, "illustration"),
95            Self::Text => write!(f, "text"),
96            Self::Screenshot => write!(f, "screenshot"),
97            Self::HighFrequency => write!(f, "high_frequency"),
98            Self::LowFrequency => write!(f, "low_frequency"),
99            Self::Gradient => write!(f, "gradient"),
100            Self::Pattern => write!(f, "pattern"),
101            Self::Cgi => write!(f, "cgi"),
102            Self::Scientific => write!(f, "scientific"),
103            Self::Other => write!(f, "other"),
104        }
105    }
106}
107
108impl std::str::FromStr for ImageCategory {
109    type Err = String;
110
111    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
112        Self::from_str_loose(s).ok_or_else(|| format!("Unknown category: {s}"))
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_category_roundtrip() {
122        for cat in ImageCategory::all() {
123            let s = cat.to_string();
124            let parsed: ImageCategory = s.parse().unwrap();
125            assert_eq!(*cat, parsed);
126        }
127    }
128
129    #[test]
130    fn test_category_from_str_loose() {
131        assert_eq!(
132            ImageCategory::from_str_loose("PHOTO"),
133            Some(ImageCategory::Photo)
134        );
135        assert_eq!(
136            ImageCategory::from_str_loose("Photos"),
137            Some(ImageCategory::Photo)
138        );
139        assert_eq!(
140            ImageCategory::from_str_loose("artwork"),
141            Some(ImageCategory::Illustration)
142        );
143        assert_eq!(ImageCategory::from_str_loose("invalid"), None);
144    }
145}