rust_c3/
lib.rs

1use std::collections::HashMap;
2use std::f64::consts::LN_2;
3use std::fs;
4use std::io::Cursor;
5use std::iter::FromIterator;
6use std::io::{self};
7
8use kd_tree::{KdPoint, KdTree};
9use kolor::Vec3;
10use ndarray::Array1;
11use ndarray::Array2;
12use serde_json::{json, Value};
13
14// define your own item type.
15struct Item {
16    point: [f64; 3],
17    id: usize,
18}
19
20// implement `KdPoint` for your item type.
21impl KdPoint for Item {
22    type Scalar = f64;
23    type Dim = typenum::U3;
24    // 2 dimensional tree.
25    fn at(&self, k: usize) -> f64 { self.point[k] }
26}
27
28pub struct C3 {
29    color: Array2<i64>,
30    c: usize,
31    w: usize,
32    a: Vec<f64>,
33    t: HashMap<i64, i64>,
34    terms: Vec<String>,
35    min_e: f64,
36    max_e: f64,
37    color_count: Array1<i64>,
38    terms_count: Array1<i64>,
39    tree: KdTree<Item>,
40}
41
42// make impl public
43impl C3 {
44    pub fn new() -> C3 {
45
46        let body = include_str!("data.json");
47        let json: Value = serde_json::from_str(&body).unwrap();
48        let _colorvec: Vec<i64> = serde_json::from_value(json["color"].clone()).unwrap();
49        let rows = _colorvec.len() / 3;
50        let _color = Array2::from_shape_vec((rows, 3), _colorvec).unwrap();
51
52        let _c = _color.shape()[0];
53
54
55        let _a: Vec<f64> = serde_json::from_value(json["A"].clone()).unwrap();
56        let mut _t = HashMap::new();
57        let t_vec: Vec<i64> = serde_json::from_value(json["T"].clone()).unwrap();
58
59
60        // Iterate over every two elements in the vector
61        for pair in t_vec.chunks(2) {
62            _t.insert(pair[0], pair[1]);
63        }
64        let tmp_vec = vec!["green", "blue", "purple", "red", "pink", "yellow", "orange", "brown", "teal", "lightblue", "grey", "limegreen", "magenta", "lightgreen", "brightgreen", "skyblue", "cyan", "turquoise", "darkblue", "darkgreen", "aqua", "olive", "navyblue", "lavender", "fuchsia", "black", "royalblue", "violet", "hotpink", "tan", "forestgreen", "lightpurple", "neongreen", "yellowgreen", "maroon", "darkpurple", "salmon", "peach", "beige", "lime", "seafoamgreen", "mustard", "brightblue", "lilac", "seagreen", "palegreen", "bluegreen", "mint", "lightbrown", "mauve", "darkred", "greyblue", "burntorange", "darkpink", "indigo", "periwinkle", "bluegrey", "lightpink", "aquamarine", "gold", "brightpurple", "grassgreen", "redorange", "bluepurple", "greygreen", "kellygreen", "puke", "rose", "darkteal", "babyblue", "paleblue", "greenyellow", "brickred", "lightgrey", "darkgrey", "white", "brightpink", "chartreuse", "purpleblue", "royalpurple", "burgundy", "goldenrod", "darkbrown", "lightorange", "darkorange", "redbrown", "paleyellow", "plum", "offwhite", "pinkpurple", "darkyellow", "lightyellow", "mustardyellow", "brightred", "peagreen", "khaki", "orangered", "crimson", "deepblue", "springgreen", "cream", "palepink", "yelloworange", "deeppurple", "pinkred", "pastelgreen", "sand", "rust", "lightred", "taupe", "armygreen", "robinseggblue", "huntergreen", "greenblue", "lightteal", "cerulean", "flesh", "orangebrown", "slateblue", "slate", "coral", "blueviolet", "ochre", "leafgreen", "electricblue", "seablue", "midnightblue", "steelblue", "brick", "palepurple", "mediumblue", "burntsienna", "darkmagenta", "eggplant", "sage", "darkturquoise", "puce", "bloodred", "neonpurple", "mossgreen", "terracotta", "oceanblue", "yellowbrown", "brightyellow", "dustyrose", "applegreen", "neonpink", "skin", "cornflowerblue", "lightturquoise", "wine", "deepred", "azure"];
65        let _terms: Vec<String> = tmp_vec.iter().map(|s| s.to_string()).collect();
66
67
68        let _w = _terms.len();
69        let mut color_count: Array1<i64> = Array1::zeros(_c);
70        let mut terms_count: Array1<i64> = Array1::zeros(_w);
71        for key in _t.keys() {
72            let mut v = 0;
73            if let Some(x) = _t.get(key) {
74                v = *x;
75            }
76            color_count[(*key as f64 / _w as f64).floor() as usize] += v;
77            terms_count[(*key % _w as i64) as usize] += v;
78        }
79        let pts = _color
80            .outer_iter()
81            .enumerate()
82            .map(|(i, row)| Item { point: [row[0] as f64, row[1] as f64, row[2] as f64], id: i })
83            .collect();
84        let tree: KdTree<Item> = KdTree::build_by_ordered_float(pts);
85        // make this a result
86        C3 {
87            c: _c,
88            color: _color,
89            a: _a,
90            t: _t,
91            w: _w,
92            terms: _terms,
93            min_e: -4.5,
94            max_e: 0.0,
95            color_count,
96            terms_count,
97            tree,
98        }
99    }
100    fn color_entropy(&self, c: usize) -> f64 {
101        let mut h: f64 = 0.0;
102        for w in 0..self.w {
103            let val = c as i64 * self.w as i64 + w as i64;
104            let mut p = 0.0;
105            if let Some(x) = self.t.get(&val as &i64) {
106                p = *x as f64 / self.color_count[c] as f64;
107            }
108            if p > 0.0 {
109                h += p * f64::ln(p) / LN_2;
110            }
111        }
112        h
113    }
114    fn color_related_terms(
115        &self,
116        c: usize,
117        limit: Option<usize>,
118        min_count: Option<usize>,
119        salience_threshold: Option<f64>,
120    ) -> Vec<HashMap<&str, f64>> {
121        let cc = c * self.w;
122        let mut list = Vec::new();
123        let mut sum = 0.0;
124        for w in 0..self.w {
125            if self.t.contains_key(&(cc as i64 + w as i64)) {
126                sum += self.t[&(cc as i64 + w as i64)] as f64;
127                list.push(HashMap::from_iter([
128                    ("index", w as f64),
129                    ("score", self.t[&(cc as i64 + w as i64)] as f64),
130                ]));
131            }
132        }
133        let mut filtered_list = list.iter().map(|x: &HashMap<&str, f64>| {
134            let score = x["score"] / sum;
135            let index = x["index"];
136            HashMap::from_iter([("score", score), ("index", index)])
137        }).collect::<Vec<HashMap<&str, f64>>>();
138
139        if let Some(threshold) = salience_threshold {
140            filtered_list = filtered_list.into_iter().filter(|x: &HashMap<&str, f64>| x["score"] > threshold).collect();
141        }
142        if let Some(min_count) = min_count {
143            filtered_list = filtered_list
144                .into_iter()
145                .filter(|x| self.terms_count[x["index"] as usize] > min_count as i64)
146                .collect();
147        }
148        filtered_list.sort_by(|a, b| b["score"].partial_cmp(&a["score"]).unwrap());
149        if let Some(limit) = limit {
150            filtered_list.truncate(limit);
151        }
152        filtered_list
153    }
154    pub(crate) fn color_cosine(
155        &self,
156        a: usize,
157        b: usize,
158    ) -> f64 {
159        let mut sa = 0.0;
160        let mut sb = 0.0;
161        let mut sc = 0.0;
162        for w in 0..self.w {
163            let mut ta = 0.0;
164            let mut tb = 0.0;
165            if let Some(val) = self.t.get(&((a * self.w + w) as i64)) {
166                ta = *val as f64;
167            }
168            if let Some(val) = self.t.get(&((b * self.w + w) as i64)) {
169                tb = *val as f64;
170            }
171            sa += ta * ta;
172            sb += tb * tb;
173            sc += ta * tb;
174        }
175        sc / (sa.sqrt() * sb.sqrt())
176    }
177    fn color_index(&self, c: [f64; 3]) -> usize {
178        // let nearest_neighbor = self.tree
179        //     Query tree for closest c colors
180        let found = self.tree.nearest(&c /* coord */).unwrap();
181        let item = found.item.id;
182        item
183    }
184
185    fn color(&self, _x: [f64; 3]) -> HashMap<&str, f64> {
186        let c = self.color_index(_x);
187        let h = (self.color_entropy(c) - self.min_e) / (self.max_e - self.min_e);
188        let mut map: HashMap<&str, f64> = HashMap::new();
189        map.insert("c", c as f64);
190        map.insert("h", h);
191        map
192    }
193
194    fn analyze_palette(&self, palette: Array2<f64>) -> Vec<HashMap<&str, f64>> {
195        palette
196            .outer_iter()
197            .map(|row| self.color([row[0], row[1], row[2]]))
198            .collect()
199    }
200    fn get_palette_terms(&self, palette: Array2<f64>, color_term_limit: usize) -> Vec<Vec<HashMap<&str, f64>>> {
201        let mut terms = Vec::new();
202        for row in palette.outer_iter() {
203            let c = self.color_index([row[0], row[1], row[2]]);
204            let related_terms = self.color_related_terms(c, Some(color_term_limit), None, None);
205            terms.push(related_terms);
206        }
207        terms
208    }
209    fn compute_color_name_distance_matrix(&self, data: Vec<HashMap<&str, f64>>) -> Array2<f64> {
210        let n = data.len();
211        let mut matrix = Array2::zeros((n, n));
212
213        for i in 0..n {
214            for j in 0..i {
215                let cosine_distance = 1.0 - self.color_cosine(*data[i].get("c").unwrap() as usize, *data[j].get("c").unwrap() as usize);
216                matrix[[i, j]] = cosine_distance;
217                matrix[[j, i]] = cosine_distance;
218            }
219        }
220        matrix
221    }
222}
223//
224// fn main() {
225// //     Create new instance of C3
226//     let mut c_3 = C3::new();
227//     // -2.9358562554686025 should be value
228//     let ce_test = c_3.color_entropy(7271);
229//     println!("Color Entropy Test: {}", ce_test);
230//     // [{'score': 0.31417624521072796, 'index': 4}, {'score': 0.1839080459770115, 'index': 12}, {'score': 0.13409961685823754, 'index': 2}, {'score': 0.1111111111111111, 'index': 24}, {'score': 0.09961685823754789, 'index': 28}, {'score': 0.034482758620689655, 'index': 76}, {'score': 0.02681992337164751, 'index': 60}, {'score': 0.019157088122605363, 'index': 27}, {'score': 0.019157088122605363, 'index': 31}, {'score': 0.019157088122605363, 'index': 89}]
231//     let related_terms = c_3.color_related_terms(7271, Some(10), None, None);
232//     println!("Related Terms Test: {:?}", related_terms);
233//     //0.009036541917907336
234//     let cosine_test = c_3.color_cosine(2173, 7271);
235//     println!("Cosine Test: {}", cosine_test);
236//     // Should be 7271
237//     let color_index_test = c_3.color_index([60.3, 98.2, -60.8]);
238//     println!("Color Index Test: {:?}", color_index_test);
239//     let palette = Array2::from_shape_vec((3, 3), vec![
240//         60.32273214, 98.2353325, -60.84232404,
241//         79.42618245, -1.22650957, -19.14108948,
242//         66.88027726, 43.42296322, 71.85391542,
243//     ]).unwrap();
244//     let analyzed_palette = c_3.analyze_palette(palette.clone());
245//     println!("Analyzed Palette: {:?}", analyzed_palette);
246//     let palette_terms = c_3.get_palette_terms(palette.clone(), 10);
247//     println!("Analyzed Palette Terms: {:?}", palette_terms);
248//     let cosine_matrix = c_3.compute_color_name_distance_matrix(analyzed_palette);
249//     println!("Cosine Matrix: {:?}", cosine_matrix[[2, 1]]);
250// //     print shape of cosine matrix
251//     println!("Cosine Matrix: {:?}", cosine_matrix.shape());
252// }