image_palette/
lib.rs

1use std::{cell::RefCell, collections::HashMap, rc::Rc};
2
3use error::ImageError;
4use image::{
5    DynamicImage,
6    ImageError::{IoError, Unsupported},
7    RgbImage, RgbaImage,
8};
9
10mod error;
11
12/// Open the image located at the path specified, return 16 dominant colors.
13///
14/// # Examples
15/// ```
16/// let colors = image_palette::load("test.jpg").unwrap();
17///
18/// for item in colors {
19///   println!("{}:{}", item.color(), item.count());
20/// }
21/// ```
22pub fn load(path: &str) -> Result<Vec<Record>, ImageError> {
23    OcTree::load_with_maxcolor(path, 16)
24}
25
26/// Open the image located at the path specified, return {max_color} dominant colors.
27///
28/// # Examples
29/// ```
30/// let colors = image_palette::load_with_maxcolor("test.jpg", 32).unwrap();
31///
32/// for item in colors {
33///   println!("{}:{}", item.color(), item.count());
34/// }
35/// ```
36pub fn load_with_maxcolor(path: &str, max_color: u32) -> Result<Vec<Record>, ImageError> {
37    OcTree::load_with_maxcolor(path, max_color)
38}
39
40#[derive(Debug)]
41struct OcTree {
42    leaf_num: u32,
43    to_reduce: [Vec<Rc<RefCell<Node>>>; 8],
44    max_color: u32,
45}
46
47impl OcTree {
48    fn load_with_maxcolor(path: &str, max_color: u32) -> Result<Vec<Record>, ImageError> {
49        const ARRAY_REPEAT_VALUE: Vec<Rc<RefCell<Node>>> = Vec::new();
50        let mut tree = OcTree {
51            leaf_num: 0,
52            to_reduce: [ARRAY_REPEAT_VALUE; 8],
53            max_color,
54        };
55
56        let image = image::open(&path).map_err(|error| match error {
57            Unsupported(error) => ImageError::UnsupportedFile(error),
58            IoError(error) => ImageError::IoError(error),
59            error => ImageError::Unknown(error),
60        })?;
61
62        let image_data = ImageData::try_from(&image)?;
63
64        let root = Node::new();
65        let root_share: Rc<RefCell<Node>> = Rc::new(RefCell::new(root));
66
67        for color in image_data.data {
68            tree.add_color(&root_share, color, 0);
69
70            while tree.leaf_num > tree.max_color {
71                tree.reduce_tree();
72            }
73        }
74
75        let mut map: HashMap<String, u32> = HashMap::new();
76        colors_stats(&root_share, &mut map);
77        let mut list = Vec::new();
78        for (color, count) in map {
79            list.push(Record { color, count });
80        }
81        list.sort_by(|a, b| b.count.cmp(&a.count));
82        Ok(list)
83    }
84
85    fn create_node(&mut self, level: usize) -> Rc<RefCell<Node>> {
86        let node = Node::new();
87        let node_share: Rc<RefCell<Node>> = Rc::new(RefCell::new(node));
88
89        if level == 7 {
90            let mut node_mut: std::cell::RefMut<Node> = node_share.borrow_mut();
91            node_mut.is_leaf = true;
92            self.leaf_num += 1;
93        } else {
94            let a: Rc<RefCell<Node>> = Rc::clone(&node_share);
95            self.to_reduce[level].push(a);
96            self.to_reduce[level].sort_by_key(|k: &Rc<RefCell<Node>>| k.borrow().pixel_count);
97        }
98
99        node_share
100    }
101
102    fn add_color(&mut self, node_share: &Rc<RefCell<Node>>, color: Color, level: usize) {
103        let mut node: std::cell::RefMut<Node> = node_share.borrow_mut();
104        if node.is_leaf {
105            node.pixel_count += 1;
106            node.r += color.0 as u32;
107            node.g += color.1 as u32;
108            node.b += color.2 as u32;
109        } else {
110            let r = color.0 >> (7 - level) & 1;
111            let g = color.1 >> (7 - level) & 1;
112            let b = color.2 >> (7 - level) & 1;
113
114            let idx = ((r << 2) + (g << 1) + b) as usize;
115
116            if node.children[idx].is_none() {
117                let child_share: Rc<RefCell<Node>> = self.create_node(level + 1);
118                node.children[idx] = Some(child_share);
119            }
120
121            self.add_color(node.children[idx].as_ref().unwrap(), color, level + 1);
122        }
123    }
124
125    fn reduce_tree(&mut self) {
126        // find the deepest level of node
127        let mut lv: isize = 6;
128
129        while lv >= 0 && self.to_reduce[lv as usize].len() == 0 {
130            lv -= 1;
131        }
132        if lv < 0 {
133            return;
134        }
135
136        let node_share = self.to_reduce[lv as usize].pop().unwrap();
137        let mut node = node_share.borrow_mut();
138
139        // merge children
140        let mut r = 0;
141        let mut g = 0;
142        let mut b = 0;
143        let mut pixel_count = 0;
144
145        for i in 0..8 {
146            if node.children[i].is_none() {
147                continue;
148            }
149            let child_share = node.children[i].as_ref().unwrap();
150            let child = child_share.borrow();
151
152            r += child.r;
153            g += child.g;
154            b += child.b;
155            pixel_count += child.pixel_count;
156            self.leaf_num -= 1;
157        }
158
159        node.is_leaf = true;
160        node.r = r;
161        node.g = g;
162        node.b = b;
163        node.pixel_count = pixel_count;
164
165        self.leaf_num += 1;
166    }
167}
168
169fn colors_stats(node_share: &Rc<RefCell<Node>>, map: &mut HashMap<String, u32>) {
170    let node = node_share.borrow_mut();
171    if node.is_leaf {
172        let r = format!("{:0>2}", format!("{:X}", node.r / node.pixel_count));
173        let g = format!("{:0>2}", format!("{:X}", node.g / node.pixel_count));
174        let b = format!("{:0>2}", format!("{:X}", node.b / node.pixel_count));
175        let color = format!("#{}{}{}", r, g, b);
176        if let Some(x) = map.get_mut(&color) {
177            *x = *x + node.pixel_count;
178        } else {
179            map.insert(color, node.pixel_count);
180        }
181    } else {
182        for i in 0..8 {
183            if node.children[i].is_some() {
184                colors_stats(node.children[i].as_ref().unwrap(), map);
185            }
186        }
187    }
188}
189
190impl TryFrom<&DynamicImage> for ImageData {
191    type Error = ImageError;
192
193    fn try_from(image: &DynamicImage) -> Result<Self, Self::Error> {
194        match image {
195            image::DynamicImage::ImageRgb8(image) => Ok(ImageData::from(image)),
196            image::DynamicImage::ImageRgba8(image) => Ok(ImageData::from(image)),
197            _ => Err(ImageError::UnsupportedType(image.color())),
198        }
199    }
200}
201
202impl From<&RgbImage> for ImageData {
203    fn from(image: &RgbImage) -> Self {
204        let (width, height) = image.dimensions();
205        let size = (width * height) as usize;
206
207        let data = image
208            .pixels()
209            .fold(Vec::with_capacity(size), |mut pixels, pixel| {
210                pixels.push(Color(pixel[0], pixel[1], pixel[2]));
211                pixels
212            });
213
214        Self { data }
215    }
216}
217
218impl From<&RgbaImage> for ImageData {
219    fn from(image: &RgbaImage) -> Self {
220        let (width, height) = image.dimensions();
221        let size = (width * height) as usize;
222
223        let data = image.pixels().filter(|pixels| pixels[3] > 0).fold(
224            Vec::with_capacity(size),
225            |mut pixels, pixel| {
226                pixels.push(Color(pixel[0], pixel[1], pixel[2]));
227                pixels
228            },
229        );
230
231        Self { data }
232    }
233}
234
235struct ImageData {
236    data: Vec<Color>,
237}
238
239#[derive(Debug)]
240struct Node {
241    is_leaf: bool,
242    r: u32,
243    g: u32,
244    b: u32,
245    pixel_count: u32,
246    children: [Option<Rc<RefCell<Node>>>; 8],
247}
248
249impl Node {
250    fn new() -> Node {
251        const ARRAY_REPEAT_VALUE: Option<Rc<RefCell<Node>>> = None;
252        Node {
253            is_leaf: false,
254            r: 0,
255            g: 0,
256            b: 0,
257            pixel_count: 0,
258            children: [ARRAY_REPEAT_VALUE; 8],
259        }
260    }
261}
262
263#[derive(Debug)]
264struct Color(u8, u8, u8);
265
266#[derive(Debug)]
267pub struct Record {
268    color: String,
269    count: u32,
270}
271
272impl Record {
273    pub fn color(&self) -> &str {
274        &self.color
275    }
276    pub fn count(&self) -> u32 {
277        self.count
278    }
279}