两个值之间的颜色差异/相似度%(使用JS)

31
我需要计算两个十六进制颜色值之间的差异,以百分比值输出。首先我排除了将十六进制值转换为十进制值的方法,因为第一个值的权重要比最后一个高得多。
第二种选择是计算每个RGB值之间的差异,然后将它们相加。然而,0,0,0和30,30,30之间的差异要比0,0,0和90,0,0之间的差异小得多。 这个问题建议使用YUV,但我不知道如何使用它来确定差异。
此外,这个问题有一个很好的公式来计算差异并输出RGB值,但还不够完整。

我找到了一篇关于JavaScript中配色匹配的好文章:http://html5hub.com/exploring-color-matching-in-javascript/ - maersu
6个回答

39

对于那些只想要快速复制/粘贴的人,这里是来自antimatter15的这个仓库的代码(经过一些简化以方便使用):

function deltaE(rgbA, rgbB) {
  let labA = rgb2lab(rgbA);
  let labB = rgb2lab(rgbB);
  let deltaL = labA[0] - labB[0];
  let deltaA = labA[1] - labB[1];
  let deltaB = labA[2] - labB[2];
  let c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
  let c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
  let deltaC = c1 - c2;
  let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
  deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
  let sc = 1.0 + 0.045 * c1;
  let sh = 1.0 + 0.015 * c1;
  let deltaLKlsl = deltaL / (1.0);
  let deltaCkcsc = deltaC / (sc);
  let deltaHkhsh = deltaH / (sh);
  let i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
  return i < 0 ? 0 : Math.sqrt(i);
}

function rgb2lab(rgb){
  let r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, x, y, z;
  r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
  g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
  b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
  x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
  y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000;
  z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
  x = (x > 0.008856) ? Math.pow(x, 1/3) : (7.787 * x) + 16/116;
  y = (y > 0.008856) ? Math.pow(y, 1/3) : (7.787 * y) + 16/116;
  z = (z > 0.008856) ? Math.pow(z, 1/3) : (7.787 * z) + 16/116;
  return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)]
}

使用它,只需传入两个RGB数组:

deltaE([128, 0, 255], [128, 0, 255]); // 0
deltaE([128, 0, 255], [128, 0, 230]); // 3.175
deltaE([128, 0, 255], [128, 0, 230]); // 21.434
deltaE([0, 0, 255], [255, 0, 0]); // 61.24

enter image description here

上述表格来自这里。上述代码基于1994年的DeltaE版本。


基于它们的HSB值而不是RGB值来比较两种颜色,是否会产生更好的结果?如果是这样,那么这个函数会是什么样子呢? :) - Kawd
这真的非常有用!感谢您!我将其作为代码的一部分用于将颜色分类为颜色系列,如果 delta <10,则将颜色分组为家族。一个问题是它没有很好地考虑灰色阴影,因此我添加了一个函数来解决这个问题,然后再通过这些函数运行我的 RGB 数组。const isItGrey = (rgbArr) => { const difference = Math.max(...rgbArr) - Math.min(...rgbArr); if (difference < 10) { return true; } else { return false; } }; - kwicz

12
问题在于你想要像三维世界上的距离一样的东西,但是RGB表示法并不直观:'近似'颜色可能与'远离'颜色非常不同。
例如,取两个灰色调 c1: (120,120,120) 和 c2: (150,150,150),现在取 c3: (160,140,140),它比 c1 更接近 c2,但它是紫色的,对于眼睛来说,较暗的灰色比紫色更接近灰色。
我建议您使用HSV:颜色由“基础”颜色(色相)、饱和度和强度定义。具有相近色相的颜色确实非常接近。具有非常不同色相的颜色彼此之间没有关系(例如:黄色和绿色),但可能在(非常)低饱和度和(非常)低强度下看起来更接近。
(夜晚所有颜色都是相同的。)
由于色调分为6个块,因此 cyl = Math.floor(hue / 6) 给出了您相似性评估的第一步:如果是同一部分的圆柱体,则相当接近。 如果它们不属于同一圆柱体,则如果(h2-h1)很小,可以仍然(相当)接近,将其与(1/6)进行比较。 如果(h2-h1)> 1/6,则这可能只是颜色差异太大。

然后您可以更精确地使用(s,v)。 如果饱和度低/非常低并且/或强度低,则它们更接近。

使用支持rgb和hsv的取色器进行实验,直到您知道希望具有什么差异值。 但请注意,您无法获得“真正”的相似性度量。

您可以在此处找到一个rgb --> hsv javascript转换器:http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c


11

仅需计算欧几里得距离

var c1 = [0, 0, 0],
    c2 = [30, 30, 30],
    c3 = [90, 0, 0],
    distance = function(v1, v2){
        var i,
            d = 0;

        for (i = 0; i < v1.length; i++) {
            d += (v1[i] - v2[i])*(v1[i] - v2[i]);
        }
        return Math.sqrt(d);
    };

console.log( distance(c1, c2), distance(c1, c3), distance(c2, c3) );
//will give you 51.96152422706632 90 73.48469228349535


你读过游戏炼金术士的评论吗?我认为他指出了一些你的答案不太好的原因。基本上,更远并不意味着对人眼更加清晰可辨。 - xaxxon
4
欧几里得距离在RGB颜色空间中不起作用,但在LAB颜色空间中有效。然而,虽然 LAB 空间旨在实现感知颜色的均匀性,但它还存在缺陷!因此,欧几里得距离 dE76(字面意义上)不是很精确,尤其是对于饱和度。相比之下,dE94和dE00更准确。更多信息请参见:http://zschuessler.github.io/DeltaE/learn/ - Zachary Schuessler

10
我发布了一个用于计算三种CIE算法(de76,de94和de00)的npm / Bower软件包。 它是公共领域的,并且在Github上可用。

http://zschuessler.github.io/DeltaE/

这是一个快速入门指南:

通过npm安装

npm install delta-e

用法

// Include library
var DeltaE = require('delta-e');

// Create two test LAB color objects to compare!
var color1 = {L: 36, A: 60, B: 41};
var color2 = {L: 100, A: 40, B: 90};

// 1976 formula
console.log(DeltaE.getDeltaE76(color1, color2));

// 1994 formula
console.log(DeltaE.getDeltaE94(color1, color2));

// 2000 formula
console.log(DeltaE.getDeltaE00(color1, color2));

您需要将颜色转换为LAB格式才能使用此库。d3.js拥有出色的API来完成这项工作-我相信您也可以找到一些临时解决方案。


2
ColorWiki的第三个颜色比较规则是“永远不要试图通过使用平均因子在不同方程计算的颜色差异之间进行转换”。这是因为在数学上相近的颜色并不总是在视觉上对我们人类来说相似。
你可能正在寻找的是delta-e,它是代表两种颜色之间“距离”的单一数字。
最流行的算法如下所示,其中CIE76(又称CIE 1976或dE76)是最流行的。

每个方法都有不同的实现方式,但大部分都需要将颜色转换为比RGB更好(用于比较)的颜色模型。

维基百科上有所有的公式:http://en.wikipedia.org/wiki/Color_difference

您可以使用在线颜色计算器来检查您的工作:

  • CIE76(CIE 1976 Lab色差公式)
  • CMC l:c(CMC l:c色差公式)

最后,虽然不是JavaScript,但我开始了一个开源的C#库,可以进行一些这样的转换和计算:https://github.com/THEjoezack/ColorMine


嗨,乔。你的C#库中为什么没有弧度和角度之间的转换?例如Math.Atan2(lab1.B, aPrime1) % 360;是一个模360度的弧度值,这真的让我很困惑...请告诉我你的想法,谢谢。 - Mark Ni

1
使用 Color.js
let Color = await import("https://cdn.jsdelivr.net/npm/colorjs.io@0.0.5/dist/color.esm.js").then(m => m.default);
let color1 = new Color(`rgb(10,230,95)`);
let color2 = new Color(`rgb(100,20,130)`);
let colorDistance = color1.deltaE2000(color2);

距离为0表示颜色相同,值为100表示它们相反。
deltaE2000是当前行业标准(比顶部答案提到的1994版本更好),但Color.js还有其他算法,如deltaE76、deltaECMC和deltaEITP。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接