1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
use std::collections::HashMap;
#[cfg(feature = "kmeans")]
use crate::nih_kmeans::KMeans;
use rgb::{ComponentBytes, RGB8};
use crate::{
difference::{self, DiffFn},
ImageData,
};
pub trait Selector {
// wanted Into<ImageData> here but rustc got mad about vtable building
// because we store this as Box<dyn Selector> in Squasher and it's builder
fn select(&mut self, max_colors: usize, image: ImageData) -> Vec<RGB8>;
}
pub struct SortSelect {
tolerance: f32,
difference_fn: Box<DiffFn>,
}
impl Selector for SortSelect {
/// Pick the colors in the palette from a Vec of colors sorted by number
/// of times they occur, high to low.
fn select(&mut self, max_colours: usize, image: ImageData) -> Vec<RGB8> {
let sorted = Self::unique_and_sort(image);
let tolerance = (self.tolerance / 100.0) * 765.0;
let mut selected_colors: Vec<RGB8> = Vec::with_capacity(max_colours);
for sorted_color in sorted {
if max_colours <= selected_colors.len() {
break;
} else if selected_colors.iter().all(|selected_color| {
(self.difference_fn)(selected_color, &sorted_color) > tolerance
}) {
selected_colors.push(sorted_color);
}
}
selected_colors
}
}
impl SortSelect {
/// How different colours have to be to enter the palette. Should be between
/// 0.0 and 100.0, but is unchecked.
pub fn tolerance(mut self, percent: f32) -> Self {
self.tolerance = percent;
self
}
/// The function to use to compare colours while selecting the palette.
///
/// see the [difference] module for functions included with the crate and
/// information on implementing your own.
pub fn difference(mut self, diff_fn: &'static DiffFn) -> Self {
self.difference_fn = Box::new(diff_fn);
self
}
/// Takes an image buffer of RGB data and fill the color map
fn unique_and_sort<'a, Img>(buffer: Img) -> Vec<RGB8>
where
Img: Into<ImageData<'a>>,
{
let ImageData(rgb) = buffer.into();
let mut colors: HashMap<RGB8, usize> = HashMap::default();
//count pixels
for px in rgb {
match colors.get_mut(px) {
None => {
colors.insert(*px, 1);
}
Some(n) => *n += 1,
}
}
Self::sort(colors)
}
fn sort(map: HashMap<RGB8, usize>) -> Vec<RGB8> {
let mut sorted: Vec<(RGB8, usize)> = map.into_iter().collect();
sorted.sort_by(|(colour1, freq1), (colour2, freq2)| {
freq2
.cmp(freq1)
.then(colour2.r.cmp(&colour1.r))
.then(colour2.g.cmp(&colour1.g))
.then(colour2.b.cmp(&colour1.b))
});
sorted.into_iter().map(|(color, _count)| color).collect()
}
}
impl Default for SortSelect {
fn default() -> Self {
Self {
tolerance: 3.0,
difference_fn: Box::new(difference::rgb),
}
}
}
#[cfg(feature = "kmeans")]
#[derive(Debug, Default)]
pub struct Kmeans;
#[cfg(feature = "kmeans")]
impl Selector for Kmeans {
fn select(&mut self, max_colors: usize, image: ImageData) -> Vec<RGB8> {
let ImageData(rgb) = image;
let kmean = KMeans::new(rgb.to_vec());
kmean.get_k_colors(max_colors, max_iter)
}
}
|