1use std::collections::HashMap;
28
29use image::{imageops, DynamicImage, GenericImage, GrayImage};
30
31pub struct Solver {
33 training_data: HashMap<String, char>,
34}
35
36impl Solver {
37 pub fn new() -> Result<Self, bincode::Error> {
46 Ok(Solver {
47 training_data: bincode::deserialize(include_bytes!(
48 "../dataset.bin"
49 ))?,
50 })
51 }
52
53 pub fn resolve_image(&self, image: &DynamicImage) -> String {
70 let mut letters = extract_letters(image);
71
72 if letters.len() == 7 {
73 letters[6] = merge_images(&letters[6], &letters[0]);
74 letters.remove(0);
75 }
76
77 let mut resolved = String::new();
78
79 for img in letters {
80 let binary = img
81 .pixels()
82 .map(|pixel| if pixel.0[0] <= 1 { '1' } else { '0' })
83 .collect::<String>();
84
85 resolved.push(
86 *self
87 .training_data
88 .get(&binary)
89 .unwrap_or_else(|| self.most_similar_letter(&binary)),
90 );
91 }
92
93 resolved.to_lowercase()
94 }
95
96 fn most_similar_letter(&self, letter: &str) -> &char {
98 let mut max_letter = &' ';
99 let mut max_score = usize::MIN;
100
101 for (key, value) in &self.training_data {
102 let score = letter
103 .chars()
104 .zip(key.chars())
105 .filter(|(a, b)| a == b)
106 .count();
107
108 if score > max_score {
109 max_score = score;
110 max_letter = value;
111 }
112 }
113
114 max_letter
115 }
116}
117
118fn merge_images(img1: &GrayImage, img2: &GrayImage) -> GrayImage {
120 let (width1, height1) = img1.dimensions();
121 let (width2, height2) = img2.dimensions();
122
123 let mut merged_image =
124 GrayImage::new(width1 + width2, height1.max(height2));
125
126 merged_image.copy_from(img1, 0, 0).unwrap();
127 merged_image.copy_from(img2, width1, 0).unwrap();
128
129 merged_image
130}
131
132fn extract_letters(img: &DynamicImage) -> Vec<GrayImage> {
134 let img = imageops::grayscale(img);
135 let (width, height) = img.dimensions();
136
137 let mut rects = Vec::new();
138 let mut start = None;
139
140 for x in 0..width {
141 if (0..height).any(|y| img.get_pixel(x, y)[0] <= 1) {
142 if start.is_none() {
143 start = Some(x);
144 }
145 } else if let Some(point) = start {
146 rects.push((point, x));
147 start = None;
148 }
149 }
150
151 if let Some(point) = start {
152 rects.push((point, width));
153 }
154
155 rects
156 .iter()
157 .map(|(x1, x2)| {
158 imageops::crop_imm(&img, *x1, 0, x2 - x1, height).to_image()
159 })
160 .collect()
161}