Skip to content

Commit 63f4d99

Browse files
author
Tony Crisci
committed
use lab colorspace
1 parent 6b22d1a commit 63f4d99

File tree

2 files changed

+52
-9
lines changed

2 files changed

+52
-9
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Naming things is one of the hard things in computer science.
1010

1111
Colornamer-rs is a library written in Rust that helps you come up with a good name for a color.
1212

13-
It does this by converting the color to RGB colorspace and then finding the nearest neighbor in a list of colors.
13+
It does this by converting the color to [Lab color space](https://en.wikipedia.org/wiki/Lab_color_space) and using the Delta E formula to compare the color difference in a list of colors with known names and finding the nearest neighbor.
1414

1515
## Example
1616

@@ -38,14 +38,16 @@ The color names are derived from several lists:
3838
* Pantone
3939
* ntc - an [astounding collection](http://chir.ag/projects/ntc/) of over 1500 named colors.
4040

41-
## TODO
42-
43-
* Use [Lab color space](https://en.wikipedia.org/wiki/Lab_color_space) instead of RGB.
44-
4541
## Acknowledgements
4642

4743
This library is based on the wonderful [color-namer](https://github.com/colorjs/color-namer) library for nodejs.
4844

45+
Color difference formulas were found at [easyrgb.com](http://www.easyrgb.com/en/math.php).
46+
47+
Further reading:
48+
49+
* [Wikipedia article for color distance](https://en.wikipedia.org/wiki/Color_difference).
50+
4951
## License
5052

5153
MIT (see LICENSE)

src/color.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,52 @@ pub fn color_from_triplet(name: &'static str, t: (u8, u8, u8)) -> Color {
8181
}
8282

8383
impl Color {
84+
/// http://www.easyrgb.com/en/math.php
85+
fn to_lab(&self) -> (f32, f32, f32) {
86+
let xyz_normalize = |c: f32| {
87+
let c_normal = c / 255.0;
88+
if c_normal > 0.04045 {
89+
((c_normal + 0.055) / 1.055).powf(2.4)
90+
} else {
91+
c_normal / 12.92
92+
}
93+
};
94+
95+
let r = xyz_normalize(self.r);
96+
let g = xyz_normalize(self.g);
97+
let b = xyz_normalize(self.b);
98+
99+
let x = r * 0.4124 + g * 0.3576 + b * 0.1805;
100+
let y = r * 0.2126 + g * 0.7152 + b * 0.0722;
101+
let z = r * 0.0193 + g * 0.1192 + b * 0.9505;
102+
103+
let lab_normalize = |c: f32| {
104+
if c > 0.008856 {
105+
c.powf(1.0 / 3.0)
106+
} else {
107+
7.787 * c + 16.0 / 116.0
108+
}
109+
};
110+
111+
let lx = lab_normalize(x);
112+
let ly = lab_normalize(y);
113+
let lz = lab_normalize(z);
114+
115+
let l = 116.0 * ly - 16.0;
116+
let a = 500.0 * (lx - ly);
117+
let b = 200.0 * (ly - lz);
118+
119+
(l, a, b)
120+
}
121+
84122
pub fn distance(&self, other: &Self) -> f32 {
85-
let dr = self.r - other.r;
86-
let dg = self.g - other.g;
87-
let db = self.b - other.b;
123+
let (sl, sa, sb) = self.to_lab();
124+
let (ol, oa, ob) = other.to_lab();
125+
126+
let dl = sl - ol;
127+
let da = sa - oa;
128+
let db = sb - ob;
88129

89-
dr*dr + dg*dg + db*db
130+
(dl*dl + da*da + db*db).sqrt()
90131
}
91132
}

0 commit comments

Comments
 (0)