Skip to content

Commit f2e2933

Browse files
authored
YIQ color space; Kotsarenko/Ramos distance (#65)
* YIQ color space; Kotsarenko/Ramos distance * Add YIQ / Kotsarenko/Ramos distance to docs
1 parent 15a072c commit f2e2933

File tree

9 files changed

+140
-10
lines changed

9 files changed

+140
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
## Culori Changelog
22

3-
### 0.5.0
3+
### 0.5.1
44

5-
Removed default export from ES build, and cleaned up the build process. Added Prettier, ESLint.
5+
**Breaking:** Removed the `culori()` function, which was an alias for `culori.rgb()`. This makes the build simpler and clarifies the API a bit.
6+
7+
Cleaned up the build process; added Prettier, ESLint.
68

79
### Previously
810

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ The available modes (color spaces) are listed below. Each color space has a conv
136136
| `cubehelix` | Cubehelix color space | culori.**cubehelix**(_color_) |
137137
| `dlab` | DIN99o Lab color space | culori.**dlab**(_color_) |
138138
| `dlch` | DIN99o LCh color space | culori.**dlch**(_color_) |
139+
| `yiq` | YIQ color space | culori.**yiq**(_color_) |
139140
140141
<a name="culoriFormatter" href="#culoriFormatter">#</a> culori.**formatter**(_format = 'rgb'_) → _function (color)_ [<>](https://github.com/evercoder/culori/blob/master/src/formatter.js 'Source')
141142
@@ -228,10 +229,12 @@ samples(5).map(grays);
228229
229230
These methods are concerned to finding the [distance between two colors](https://en.wikipedia.org/wiki/Color_difference) based on various formulas. Each of these formulas will return a _function (colorA, colorB)_ that lets you measure the distance between two colors. Also available as a separate [d3 plugin](https://github.com/evercoder/d3-color-difference).
230231
231-
<a name="culoriDifferenceEuclidean" href="#culoriDifferenceEuclidean">#</a> culori.**differenceEuclidean**(_mode = 'rgb'_) [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source')
232+
<a name="culoriDifferenceEuclidean" href="#culoriDifferenceEuclidean">#</a> culori.**differenceEuclidean**(_mode = 'rgb'_, _weights = [1, 1, 1]_) [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source')
232233
233234
Returns a [Euclidean distance](https://en.wikipedia.org/wiki/Color_difference#Euclidean) function in a certain color space.
234235
236+
You can optionally assign different weights to the channels in the color space. See, for example, the [Kotsarenko/Ramos distance](#culoriDifferenceKotsarenkoRamos).
237+
235238
<a name="culoriDifferenceCie76" href="#culoriDifferenceCie76">#</a> culori.**differenceCie76**() [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source')
236239
237240
Computes the [CIE76][cie76] ΔE\*<sub>ab</sub> color difference between the colors _a_ and _b_. The computation is done in the Lab color space and it is analogous to [culori.differenceEuclidean('lab')](#culoriDifferenceEuclidean).
@@ -256,6 +259,10 @@ _Note:_ ΔE\*<sub>CMC</sub> is not considered a metric since it's not symmetrica
256259
257260
Computes the [DIN99o][din99ode] ΔE\*<sub>99o</sub> color difference between the colors _a_ and _b_. The computation is done in the [DIN99o][din99o] color space.
258261
262+
<a name="culoriDifferenceKotsarenkoRamos" href="#culoriDifferenceKotsarenkoRamos">#</a> culori.**differenceKotsarenkoRamos**() [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source')
263+
264+
Computes the [Kotsarenko/Ramos][kotsarekno-ramos] color difference between the colors _a_ and _b_. This is a weighted Euclidean distance in the [YIQ][yiq] color space.
265+
259266
#### Nearest color(s)
260267
261268
<a name="culoriNearest" href="#culoriNearest">#</a> culori.**nearest**(_colors_, _metric = differenceEuclidean()_, _accessor = identity_) → _function(color, n = 1, τ = Infinity)_ [<>](https://github.com/evercoder/culori/blob/master/src/nearest.js 'Source')
@@ -355,3 +362,5 @@ These libraries add more functionality to culori:
355362
[cmc]: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_(1984)
356363
[din99o]: https://de.wikipedia.org/wiki/DIN99-Farbraum
357364
[din99ode]: https://de.wikipedia.org/wiki/DIN99-Farbraum#Farbabstandsformel
365+
[kotsarekno-ramos]: http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf
366+
[yiq]: https://en.wikipedia.org/wiki/YIQ

src/difference.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import { getModeDefinition } from './modes';
22
import converter from './converter';
33

4-
const differenceEuclidean = (mode = 'rgb') => {
4+
const differenceEuclidean = (mode = 'rgb', weights = [1, 1, 1]) => {
55
let channels = getModeDefinition(mode).channels;
66
let conv = converter(mode);
77
return (std, smp) => {
88
let ConvStd = conv(std);
99
let ConvSmp = conv(smp);
1010
return Math.sqrt(
1111
channels.reduce(
12-
(delta, k) =>
12+
(delta, k, idx) =>
1313
// ignore alpha channel in computing the euclidean distance
1414
delta +
15-
(k === 'alpha' ? 0 : Math.pow(ConvStd[k] - ConvSmp[k], 2)),
15+
(k === 'alpha'
16+
? 0
17+
: weights[idx] * Math.pow(ConvStd[k] - ConvSmp[k], 2)),
1618
0
1719
)
1820
);
@@ -194,11 +196,26 @@ const differenceCmc = (l = 1, c = 1) => {
194196

195197
const differenceDin99o = () => differenceEuclidean('dlab');
196198

199+
/*
200+
"Measuring perceived color difference using YIQ NTSC
201+
transmission color space in mobile applications"
202+
203+
by Yuriy Kotsarenko, Fernando Ramos in:
204+
Programación Matemática y Software (2010)
205+
206+
Available at:
207+
208+
http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf
209+
*/
210+
const differenceKotsarenkoRamos = () =>
211+
differenceEuclidean('yiq', [0.5053, 0.299, 0.1957]);
212+
197213
export {
198214
differenceEuclidean,
199215
differenceCie76,
200216
differenceCie94,
201217
differenceCiede2000,
202218
differenceCmc,
203-
differenceDin99o
219+
differenceDin99o,
220+
differenceKotsarenkoRamos
204221
};

src/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import lchDef from './lch/definition';
99
import cubehelixDef from './cubehelix/definition';
1010
import dlabDef from './dlab/definition';
1111
import dlchDef from './dlch/definition';
12+
import yiqDef from './yiq/definition';
1213

1314
import { defineMode } from './modes';
1415
import converter from './converter';
@@ -24,6 +25,7 @@ defineMode(lchDef);
2425
defineMode(cubehelixDef);
2526
defineMode(dlabDef);
2627
defineMode(dlchDef);
28+
defineMode(yiqDef);
2729

2830
let rgb = converter('rgb');
2931
let lrgb = converter('lrgb');
@@ -36,6 +38,7 @@ let lch = converter('lch');
3638
let cubehelix = converter('cubehelix');
3739
let dlab = converter('dlab');
3840
let dlch = converter('dlch');
41+
let yiq = converter('yiq');
3942

4043
export {
4144
defineMode,
@@ -50,7 +53,8 @@ export {
5053
lrgb,
5154
cubehelix,
5255
dlab,
53-
dlch
56+
dlch,
57+
yiq
5458
};
5559

5660
export { default as formatter } from './formatter';
@@ -84,6 +88,7 @@ export {
8488
differenceCie94,
8589
differenceCiede2000,
8690
differenceCmc,
87-
differenceDin99o
91+
differenceDin99o,
92+
differenceKotsarenkoRamos
8893
} from './difference';
8994
export { default as colorsNamed } from './colors/named';

src/yiq/convertRgbToYiq.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import convertRgbToLrgb from '../lrgb/convertRgbToLrgb';
2+
3+
export default rgb => {
4+
let { r, g, b, alpha } = convertRgbToLrgb(rgb);
5+
let res = {
6+
mode: 'yiq',
7+
y: 0.29889531 * r + 0.58662247 * g + 0.11448223 * b,
8+
i: 0.59597799 * r - 0.2741761 * g - 0.32180189 * b,
9+
q: 0.21147017 * r - 0.52261711 * g + 0.31114694 * b
10+
};
11+
if (alpha !== undefined) res.alpha = alpha;
12+
return res;
13+
};

src/yiq/convertYiqToRgb.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import convertLrgbToRgb from '../lrgb/convertLrgbToRgb';
2+
3+
export default ({ y, i, q, alpha }) =>
4+
convertLrgbToRgb({
5+
r: y + 0.95608445 * i + 0.6208885 * q,
6+
g: y - 0.27137664 * i - 0.6486059 * q,
7+
b: y - 1.10561724 * i + 1.70250126 * q,
8+
alpha
9+
});

src/yiq/definition.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import convertRgbToYiq from './convertRgbToYiq';
2+
import convertYiqToRgb from './convertYiqToRgb';
3+
import interpolateNumber from '../interpolate/interpolateNumber';
4+
import interpolateAlpha from '../interpolate/interpolateAlpha';
5+
import interpolateFunctionLinear from '../interpolate/interpolateFunctionLinear';
6+
7+
/*
8+
YIQ Color Space
9+
10+
References
11+
----------
12+
13+
Wikipedia:
14+
https://en.wikipedia.org/wiki/YIQ
15+
16+
"Measuring perceived color difference using YIQ NTSC
17+
transmission color space in mobile applications"
18+
19+
by Yuriy Kotsarenko, Fernando Ramos in:
20+
Programación Matemática y Software (2010)
21+
22+
Available at:
23+
24+
http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf
25+
*/
26+
27+
export default {
28+
mode: 'yiq',
29+
output: {
30+
rgb: convertYiqToRgb
31+
},
32+
input: {
33+
rgb: convertRgbToYiq
34+
},
35+
channels: ['y', 'i', 'q', 'alpha'],
36+
interpolate: {
37+
y: interpolateFunctionLinear(interpolateNumber()),
38+
i: interpolateFunctionLinear(interpolateNumber()),
39+
q: interpolateFunctionLinear(interpolateNumber()),
40+
alpha: interpolateFunctionLinear(interpolateAlpha())
41+
}
42+
};

test/difference.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ let {
66
differenceCie94,
77
differenceCiede2000,
88
differenceCmc,
9+
differenceKotsarenkoRamos,
910
rgb,
1011
lab,
1112
round
@@ -119,7 +120,7 @@ tape('ciede2000 difference', function(test) {
119120
test.end();
120121
});
121122

122-
tape('cmc difference', function(test) {
123+
tape('differenceCmc', function(test) {
123124
test.equal(
124125
differenceCmc()(
125126
lab({ l: 1, a: 0, b: 0, alpha: 0.5 }),
@@ -130,3 +131,12 @@ tape('cmc difference', function(test) {
130131

131132
test.end();
132133
});
134+
135+
tape('differenceKotsarenkoRamos', function(test) {
136+
test.equal(
137+
differenceKotsarenkoRamos()('white', 'black'),
138+
0.7108445752103619
139+
);
140+
141+
test.end();
142+
});

test/yiq.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
let tape = require('tape');
2+
let culori = require('../');
3+
let { yiq, formatter } = culori;
4+
5+
let rgb = formatter('rgb');
6+
7+
tape('rgb to yiq and back', function(test) {
8+
test.deepEqual(
9+
rgb(yiq('rgb(255, 255, 255)')),
10+
'rgb(255, 255, 255)',
11+
'white'
12+
);
13+
14+
test.deepEqual(rgb(yiq('rgb(0, 0, 0)')), 'rgb(0, 0, 0)', 'black');
15+
16+
test.deepEqual(rgb(yiq('rgb(100, 0, 0)')), 'rgb(100, 0, 0)', 'red');
17+
18+
test.deepEqual(rgb(yiq('rgb(0, 120, 0)')), 'rgb(0, 120, 0)', 'blue');
19+
20+
test.deepEqual(rgb(yiq('rgb(0, 0, 89)')), 'rgb(0, 0, 89)', 'green');
21+
22+
test.end();
23+
});

0 commit comments

Comments
 (0)