symcode/acute32/
library.rs

1use std::collections::HashMap;
2
3use bit_vec::BitVec;
4use visioncortex::{BinaryImage, Sampler};
5
6use super::{Acute32SymcodeConfig, GlyphLabel, GlyphTrace, Symbol, Trace, image_diff_area};
7
8#[derive(Debug)]
9pub struct Acute32Library {
10    templates: Vec<Symbol>,
11}
12
13impl Default for Acute32Library {
14    fn default() -> Self {
15        Self { templates: vec![] }
16    }
17}
18
19impl Acute32Library {
20    pub fn is_empty(&self) -> bool {
21        self.templates.is_empty()
22    }
23
24    pub fn len(&self) -> usize {
25        self.templates.len()
26    }
27
28    pub fn get_glyph_at(&self, i: usize) -> Option<&Symbol> {
29        if i >= self.templates.len() {
30            None
31        } else {
32            Some(&self.templates[i])
33        }
34    }
35
36    pub fn get_glyph_with_label(&self, label: GlyphLabel) -> Option<&Symbol> {
37        for glyph in self.templates.iter() {
38            if glyph.label == label {
39                return Some(glyph);
40            }
41        }
42        None
43    }
44
45    pub fn print_label_and_trace(&self) -> String {
46        let list: Vec<String> = self.templates.iter().map(|glyph| {
47            format!("{:?}: {:?}\n", glyph.label, glyph.encoding.bits)
48        }).collect();
49        list.join("")
50    }
51
52    pub fn get_labels_grouped_by_trace(&self) -> String {
53        let mut map: HashMap<BitVec, Vec<GlyphLabel>> = HashMap::new();
54
55        self.templates.iter().for_each(|glyph| {
56            match map.get_mut(&glyph.encoding.bits) {
57                Some(labels) => {
58                    labels.push(glyph.label);
59                },
60                None => {
61                    map.insert(glyph.encoding.bits.clone(), vec![glyph.label]);
62                }
63            }
64        });
65
66        let list: Vec<String> = map.iter().map(|(bits, labels)| {
67            format!("{:?}: {}\n", bits, labels.len())
68        }).collect();
69
70        list.join("")
71    }
72
73    /// Takes the binary image of the template and the usize representation of the label
74    pub fn add_template(&mut self, image: BinaryImage, symcode_config: &Acute32SymcodeConfig) {
75        //let image = Sampler::resample_image(&image, symcode_config.symbol_width, symcode_config.symbol_height);
76        let label = GlyphLabel::from_usize_representation(self.templates.len());
77        //console_log_util(&format!("{:?}\n{}", label, image.to_string()));
78        self.templates.push(Symbol::from_image_label(image, label, symcode_config.stat_tolerance));
79    }
80
81    pub fn find_most_similar_glyph(&self, image: BinaryImage, symcode_config: &Acute32SymcodeConfig) -> GlyphLabel {
82        let image = &Sampler::resample_image(&image, symcode_config.symbol_width, symcode_config.symbol_height);
83        let input_encoding = &GlyphTrace::from_image(image, symcode_config.stat_tolerance);
84        //console_log_util(&format!("{:?}", input_encoding));
85
86        self.templates.iter()
87            .fold( (std::u64::MAX, GlyphLabel::Invalid),
88                |(min_error, min_label), template| {
89                    if template.encoding.diff(input_encoding) > symcode_config.max_encoding_difference {
90                        return (min_error, min_label);
91                    }
92                    let error = image_diff_area(&template.image, image);
93                    if error < min_error {
94                        (error, template.label)
95                    } else {
96                        (min_error, min_label)
97                    }
98                }
99            ).1
100    }
101}