diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d5b7b67..14066af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ ## Culori Changelog -### 0.5.0 +### 0.5.1 -Removed default export from ES build, and cleaned up the build process. Added Prettier, ESLint. +**Breaking:** Removed the `culori()` function, which was an alias for `culori.rgb()`. This makes the build simpler and clarifies the API a bit. + +Cleaned up the build process; added Prettier, ESLint. ### Previously diff --git a/README.md b/README.md index c3261b45..22599745 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ The available modes (color spaces) are listed below. Each color space has a conv | `cubehelix` | Cubehelix color space | culori.**cubehelix**(_color_) | | `dlab` | DIN99o Lab color space | culori.**dlab**(_color_) | | `dlch` | DIN99o LCh color space | culori.**dlch**(_color_) | +| `yiq` | YIQ color space | culori.**yiq**(_color_) | # culori.**formatter**(_format = 'rgb'_) → _function (color)_ [<>](https://github.com/evercoder/culori/blob/master/src/formatter.js 'Source') @@ -228,10 +229,12 @@ samples(5).map(grays); 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). -# culori.**differenceEuclidean**(_mode = 'rgb'_) [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source') +# culori.**differenceEuclidean**(_mode = 'rgb'_, _weights = [1, 1, 1]_) [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source') Returns a [Euclidean distance](https://en.wikipedia.org/wiki/Color_difference#Euclidean) function in a certain color space. +You can optionally assign different weights to the channels in the color space. See, for example, the [Kotsarenko/Ramos distance](#culoriDifferenceKotsarenkoRamos). + # culori.**differenceCie76**() [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source') Computes the [CIE76][cie76] ΔE\*ab 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\*CMC is not considered a metric since it's not symmetrica Computes the [DIN99o][din99ode] ΔE\*99o color difference between the colors _a_ and _b_. The computation is done in the [DIN99o][din99o] color space. +# culori.**differenceKotsarenkoRamos**() [<>](https://github.com/evercoder/culori/blob/master/src/difference.js 'Source') + +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. + #### Nearest color(s) # 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: [cmc]: https://en.wikipedia.org/wiki/Color_difference#CMC_l:c_(1984) [din99o]: https://de.wikipedia.org/wiki/DIN99-Farbraum [din99ode]: https://de.wikipedia.org/wiki/DIN99-Farbraum#Farbabstandsformel +[kotsarekno-ramos]: http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf +[yiq]: https://en.wikipedia.org/wiki/YIQ diff --git a/src/difference.js b/src/difference.js index e05b95d4..4b5d28ff 100644 --- a/src/difference.js +++ b/src/difference.js @@ -1,7 +1,7 @@ import { getModeDefinition } from './modes'; import converter from './converter'; -const differenceEuclidean = (mode = 'rgb') => { +const differenceEuclidean = (mode = 'rgb', weights = [1, 1, 1]) => { let channels = getModeDefinition(mode).channels; let conv = converter(mode); return (std, smp) => { @@ -9,10 +9,12 @@ const differenceEuclidean = (mode = 'rgb') => { let ConvSmp = conv(smp); return Math.sqrt( channels.reduce( - (delta, k) => + (delta, k, idx) => // ignore alpha channel in computing the euclidean distance delta + - (k === 'alpha' ? 0 : Math.pow(ConvStd[k] - ConvSmp[k], 2)), + (k === 'alpha' + ? 0 + : weights[idx] * Math.pow(ConvStd[k] - ConvSmp[k], 2)), 0 ) ); @@ -194,11 +196,26 @@ const differenceCmc = (l = 1, c = 1) => { const differenceDin99o = () => differenceEuclidean('dlab'); +/* + "Measuring perceived color difference using YIQ NTSC + transmission color space in mobile applications" + + by Yuriy Kotsarenko, Fernando Ramos in: + Programación Matemática y Software (2010) + + Available at: + + http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf + */ +const differenceKotsarenkoRamos = () => + differenceEuclidean('yiq', [0.5053, 0.299, 0.1957]); + export { differenceEuclidean, differenceCie76, differenceCie94, differenceCiede2000, differenceCmc, - differenceDin99o + differenceDin99o, + differenceKotsarenkoRamos }; diff --git a/src/index.js b/src/index.js index b8a4dae8..2991221f 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ import lchDef from './lch/definition'; import cubehelixDef from './cubehelix/definition'; import dlabDef from './dlab/definition'; import dlchDef from './dlch/definition'; +import yiqDef from './yiq/definition'; import { defineMode } from './modes'; import converter from './converter'; @@ -24,6 +25,7 @@ defineMode(lchDef); defineMode(cubehelixDef); defineMode(dlabDef); defineMode(dlchDef); +defineMode(yiqDef); let rgb = converter('rgb'); let lrgb = converter('lrgb'); @@ -36,6 +38,7 @@ let lch = converter('lch'); let cubehelix = converter('cubehelix'); let dlab = converter('dlab'); let dlch = converter('dlch'); +let yiq = converter('yiq'); export { defineMode, @@ -50,7 +53,8 @@ export { lrgb, cubehelix, dlab, - dlch + dlch, + yiq }; export { default as formatter } from './formatter'; @@ -84,6 +88,7 @@ export { differenceCie94, differenceCiede2000, differenceCmc, - differenceDin99o + differenceDin99o, + differenceKotsarenkoRamos } from './difference'; export { default as colorsNamed } from './colors/named'; diff --git a/src/yiq/convertRgbToYiq.js b/src/yiq/convertRgbToYiq.js new file mode 100644 index 00000000..71e95db0 --- /dev/null +++ b/src/yiq/convertRgbToYiq.js @@ -0,0 +1,13 @@ +import convertRgbToLrgb from '../lrgb/convertRgbToLrgb'; + +export default rgb => { + let { r, g, b, alpha } = convertRgbToLrgb(rgb); + let res = { + mode: 'yiq', + y: 0.29889531 * r + 0.58662247 * g + 0.11448223 * b, + i: 0.59597799 * r - 0.2741761 * g - 0.32180189 * b, + q: 0.21147017 * r - 0.52261711 * g + 0.31114694 * b + }; + if (alpha !== undefined) res.alpha = alpha; + return res; +}; diff --git a/src/yiq/convertYiqToRgb.js b/src/yiq/convertYiqToRgb.js new file mode 100644 index 00000000..d555363d --- /dev/null +++ b/src/yiq/convertYiqToRgb.js @@ -0,0 +1,9 @@ +import convertLrgbToRgb from '../lrgb/convertLrgbToRgb'; + +export default ({ y, i, q, alpha }) => + convertLrgbToRgb({ + r: y + 0.95608445 * i + 0.6208885 * q, + g: y - 0.27137664 * i - 0.6486059 * q, + b: y - 1.10561724 * i + 1.70250126 * q, + alpha + }); diff --git a/src/yiq/definition.js b/src/yiq/definition.js new file mode 100644 index 00000000..e94bd607 --- /dev/null +++ b/src/yiq/definition.js @@ -0,0 +1,42 @@ +import convertRgbToYiq from './convertRgbToYiq'; +import convertYiqToRgb from './convertYiqToRgb'; +import interpolateNumber from '../interpolate/interpolateNumber'; +import interpolateAlpha from '../interpolate/interpolateAlpha'; +import interpolateFunctionLinear from '../interpolate/interpolateFunctionLinear'; + +/* + YIQ Color Space + + References + ---------- + + Wikipedia: + https://en.wikipedia.org/wiki/YIQ + + "Measuring perceived color difference using YIQ NTSC + transmission color space in mobile applications" + + by Yuriy Kotsarenko, Fernando Ramos in: + Programación Matemática y Software (2010) + + Available at: + + http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf + */ + +export default { + mode: 'yiq', + output: { + rgb: convertYiqToRgb + }, + input: { + rgb: convertRgbToYiq + }, + channels: ['y', 'i', 'q', 'alpha'], + interpolate: { + y: interpolateFunctionLinear(interpolateNumber()), + i: interpolateFunctionLinear(interpolateNumber()), + q: interpolateFunctionLinear(interpolateNumber()), + alpha: interpolateFunctionLinear(interpolateAlpha()) + } +}; diff --git a/test/difference.js b/test/difference.js index f2d05586..bfb3c4e5 100644 --- a/test/difference.js +++ b/test/difference.js @@ -6,6 +6,7 @@ let { differenceCie94, differenceCiede2000, differenceCmc, + differenceKotsarenkoRamos, rgb, lab, round @@ -119,7 +120,7 @@ tape('ciede2000 difference', function(test) { test.end(); }); -tape('cmc difference', function(test) { +tape('differenceCmc', function(test) { test.equal( differenceCmc()( lab({ l: 1, a: 0, b: 0, alpha: 0.5 }), @@ -130,3 +131,12 @@ tape('cmc difference', function(test) { test.end(); }); + +tape('differenceKotsarenkoRamos', function(test) { + test.equal( + differenceKotsarenkoRamos()('white', 'black'), + 0.7108445752103619 + ); + + test.end(); +}); diff --git a/test/yiq.js b/test/yiq.js new file mode 100644 index 00000000..d602c282 --- /dev/null +++ b/test/yiq.js @@ -0,0 +1,23 @@ +let tape = require('tape'); +let culori = require('../'); +let { yiq, formatter } = culori; + +let rgb = formatter('rgb'); + +tape('rgb to yiq and back', function(test) { + test.deepEqual( + rgb(yiq('rgb(255, 255, 255)')), + 'rgb(255, 255, 255)', + 'white' + ); + + test.deepEqual(rgb(yiq('rgb(0, 0, 0)')), 'rgb(0, 0, 0)', 'black'); + + test.deepEqual(rgb(yiq('rgb(100, 0, 0)')), 'rgb(100, 0, 0)', 'red'); + + test.deepEqual(rgb(yiq('rgb(0, 120, 0)')), 'rgb(0, 120, 0)', 'blue'); + + test.deepEqual(rgb(yiq('rgb(0, 0, 89)')), 'rgb(0, 0, 89)', 'green'); + + test.end(); +});