在C#中比较RGB颜色

32

我试图找到一种比较两个颜色相似程度的方法。但我似乎找不到相关的资源,因此希望在此得到指点。

理想情况下,我希望得到一个分数来表明它们有多相似。例如,从0到100,其中100表示相等,0表示完全不同。

谢谢!

编辑:

从答案中了解更多关于颜色的知识后,我发现我的问题有点含糊不清。我将尝试解释我需要这个的原因。

我有一个800x600大小应用程序窗口的像素数据(位置和颜色),所以我可以通过检查每个x间隔来找出某个窗口是否打开。

然而,当应用程序被调整大小时,这种方法就会失效(内容被缩放而不是移动)。我可以计算像素的移动位置,但由于取整和反锯齿处理,颜色可能会略有不同。

Pieter的解决方案对我来说已经足够好了,在这种情况下,尽管所有其他回答也非常有帮助,所以我为每个人点了赞。我认为ColorEye的答案是最精确的,从专业的角度看,因此我将它标记为答案。


5
你对“alike”的定义是什么? - Klaus Byskov Pedersen
你应该真正告诉我们你想在这里实现什么?你是根据人眼感知进行差异化比较,还是需要其他的操作? - Daniel Mošmondor
8个回答

26
你正在寻找的是称为 Delta-E 的内容。

http://www.colorwiki.com/wiki/Delta_E:_The_Color_Difference

这是LAB色彩空间中两种颜色之间的距离。据说人眼无法分辨1 DeltaE以下的颜色(但我发现我的眼睛可以分辨出1 DeltaE以下的颜色,每个人都不同)。有四个公式可以计算“颜色差异”:
- Delta E(CIE 1976) - Delta E(CIE 1994) - Delta E(CIE 2000) - Delta E(CMC)
请查看本网站上的数学链接:

因此,正确的答案是使用给定的公式将RGB转换为LAB,然后使用DeltaE 1976来确定您的颜色之间的“差异”。结果为0表示颜色相同。任何高于0的值都可以根据规则判断:“大多数人无法区分1或更小的Delta E”。


2
感谢您的回复,我担心这会变得非常复杂。我会标记您的答案,因为它最准确,虽然我可能会选择类似Pieter的解决方案。 - SaphuA
1
这个网站有一些有用的转换算法(请参见http://www.easyrgb.com/index.php?X=MATH和http://www.easyrgb.com/index.php?X=DELT)。 - rsbarro
1
请注意,在Lab颜色空间中,CIE 1976仅是点之间的欧几里得距离,因此DE = sqrt((L2-L1)^2 + (a2-a1)^2 + (b2-b1)^2) - hruske

19

有一个开源的 .net 库可以让你轻松实现这个功能: https://github.com/hvalidi/ColorMine

比较颜色最常用的方法是CIE76

var a = new Rgb { R = 149, G = 13, B = 12 }
var b = new Rgb { R = 255, G = 13, B = 12 }

var deltaE = a.Compare(b,new Cie1976Comparison());

14

不同颜色对人眼有不同的影响力,因此可以使用它们的权重将颜色转换为灰度:

灰度值 = 0.11 * 蓝色值 + 0.59 * 绿色值 + 0.30 * 红色值

然后你可以计算两个颜色之间的差异:

颜色差异 = (灰度值1 - 灰度值2) * 100.0 / 255.0

颜色差异的范围在0-100之间。

这种方法通常用于计算图像处理中的图像差异,非常简单易用。

- 编辑: 这个公式非常简单,甚至在商业应用中也很实用。 如果你想深入了解更多,可以查看一些被称为CIE1976、CIE1994、CIE2000和CMC的颜色差异方法。 在这里你可以找到更详细的信息: http://en.wikipedia.org/wiki/Color_difference


2
没错,但它很基础,在现实世界中运作良好。编辑了答案并添加了更多信息。 - honibis
为了让结果的范围在0到100之间,除数应该是255而不是256。我会修正答案。 - Rev
1
范围不能是0-100,因为如果您有RGB =(0,0,0)并将其与另一种颜色进行比较,则结果始终为负数。因此,实际范围应该是-100至100,除非文本中缺少某些信息。 - Devolus

11

类似这样:

    public static int CompareColors(Color a, Color b)
    {
        return 100 * (int)(
            1.0 - ((double)(
                Math.Abs(a.R - b.R) +
                Math.Abs(a.G - b.G) +
                Math.Abs(a.B - b.B)
            ) / (256.0 * 3))
        );
    }

谢谢,我想我现在可以从这个开始工作了,尽管我需要看看这个有多准确。不过你的代码中有一些错误(256 应该是 255,而将结果强制转换为 int 也不是很聪明 :D)。 - SaphuA
2
虽然在数学上是正确的,但这并不是一个好主意,因为它没有考虑到颜色的感知方式。你可以很容易地找到一些相似的颜色对,但它们会得到低分,也可以找到一些不相似的颜色对,但它们会得到高分。 - Hannes Ovrén
是的,你说得对。你可以全力以赴,但这只是一种快速而简单的获取近似差异的方法。我认为“正确”的比较方法需要几页 :). - Pieter van Ginkel
我不同意那个给这个答案投反对票的人。虽然不如ColorEyes的回答准确,但对我仍然有用。 - SaphuA
4
在某些情况下,这可能是“正确”的,但仍然是非常错误的。举个快速的例子,颜色0x7f0000和0x007f00将产生(大约)与比较0xb2b2b2和0xffffff相同的结果。换句话说:鲜艳的红色和鲜艳的绿色被认为与白色和相当浅的灰色一样接近。这不是您想要的结果。 - Hannes Ovrén

11

将RGB颜色转换为HSL颜色空间通常会产生良好的效果。请查看维基百科上的转换公式。您需要为H(颜色)、S(深度)和L(亮度)之间的差异分配权重。


2
权重 L 肯定比其他的重。相较于颜色的变化,我们的眼睛对亮度的变化更加敏感。 - Brad

9

发现了一种有趣的方法,叫做颜色度量并将其适用于C#。

public static double ColourDistance(Color e1, Color e2)
{
    long rmean = ((long)e1.R + (long)e2.R) / 2;
    long r = (long)e1.R - (long)e2.R;
    long g = (long)e1.G - (long)e2.G;
    long b = (long)e1.B - (long)e2.B;
    return Math.Sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8));
}

7
颜色感知取决于许多因素,并且相似性可以用许多方式来衡量。仅比较R、G和B分量的相似性通常会得出人类不同意的结果。
这里有一些关于颜色比较的 维基百科上的一般材料,以及使用C#中的自然色彩空间的操作方法在这个问题中。

0

我已经将Bruce Lindbloom网页上的DeltaE2000代码翻译成了C语言。

在这里:

     //
     //  deltae2000.c
     //
     //  Translated by Dr Cube on 10/1/16.
     //  Translated to C from this javascript code written by Bruce LindBloom:
     //    http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html
     //    http://www.brucelindbloom.com/javascript/ColorDiff.js

     #include <stdio.h>
     #include <math.h>

     #define Lab2k struct Lab2kStruct
     Lab2k
     {
        float L;
        float a;
        float b;
     };

     // function expects Lab where: 0 >= L <=100.0 , -100 >=a <= 100.0  and  -100 >= b <= 100.0

     float
     DeltaE2000(Lab2k Lab1,Lab2k Lab2)
     {
        float kL = 1.0;
        float kC = 1.0;
        float kH = 1.0;
        float lBarPrime = 0.5 * (Lab1.L + Lab2.L);
        float c1 = sqrtf(Lab1.a * Lab1.a + Lab1.b * Lab1.b);
        float c2 = sqrtf(Lab2.a * Lab2.a + Lab2.b * Lab2.b);
        float cBar = 0.5 * (c1 + c2);
        float cBar7 = cBar * cBar * cBar * cBar * cBar * cBar * cBar;
        float g = 0.5 * (1.0 - sqrtf(cBar7 / (cBar7 + 6103515625.0)));  /* 6103515625 = 25^7 */
        float a1Prime = Lab1.a * (1.0 + g);
        float a2Prime = Lab2.a * (1.0 + g);
        float c1Prime = sqrtf(a1Prime * a1Prime + Lab1.b * Lab1.b);
        float c2Prime = sqrtf(a2Prime * a2Prime + Lab2.b * Lab2.b);
        float cBarPrime = 0.5 * (c1Prime + c2Prime);
        float h1Prime = (atan2f(Lab1.b, a1Prime) * 180.0) / M_PI;
        float dhPrime; // not initialized on purpose

        if (h1Prime < 0.0)
           h1Prime += 360.0;
        float h2Prime = (atan2f(Lab2.b, a2Prime) * 180.0) / M_PI;
        if (h2Prime < 0.0)
           h2Prime += 360.0;
        float hBarPrime = (fabsf(h1Prime - h2Prime) > 180.0) ? (0.5 * (h1Prime + h2Prime + 360.0)) : (0.5 * (h1Prime + h2Prime));
        float t = 1.0 -
        0.17 * cosf(M_PI * (      hBarPrime - 30.0) / 180.0) +
        0.24 * cosf(M_PI * (2.0 * hBarPrime       ) / 180.0) +
        0.32 * cosf(M_PI * (3.0 * hBarPrime +  6.0) / 180.0) -
        0.20 * cosf(M_PI * (4.0 * hBarPrime - 63.0) / 180.0);
        if (fabsf(h2Prime - h1Prime) <= 180.0)
           dhPrime = h2Prime - h1Prime;
        else
           dhPrime = (h2Prime <= h1Prime) ? (h2Prime - h1Prime + 360.0) : (h2Prime - h1Prime - 360.0);
        float dLPrime = Lab2.L - Lab1.L;
        float dCPrime = c2Prime - c1Prime;
        float dHPrime = 2.0 * sqrtf(c1Prime * c2Prime) * sinf(M_PI * (0.5 * dhPrime) / 180.0);
        float sL = 1.0 + ((0.015 * (lBarPrime - 50.0) * (lBarPrime - 50.0)) / sqrtf(20.0 + (lBarPrime - 50.0) * (lBarPrime - 50.0)));
        float sC = 1.0 + 0.045 * cBarPrime;
        float sH = 1.0 + 0.015 * cBarPrime * t;
        float dTheta = 30.0 * expf(-((hBarPrime - 275.0) / 25.0) * ((hBarPrime - 275.0) / 25.0));
        float cBarPrime7 = cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime;
        float rC = sqrtf(cBarPrime7 / (cBarPrime7 + 6103515625.0));
        float rT = -2.0 * rC * sinf(M_PI * (2.0 * dTheta) / 180.0);
        return(sqrtf(
                           (dLPrime / (kL * sL)) * (dLPrime / (kL * sL)) +
                           (dCPrime / (kC * sC)) * (dCPrime / (kC * sC)) +
                           (dHPrime / (kH * sH)) * (dHPrime / (kH * sH)) +
                           (dCPrime / (kC * sC)) * (dHPrime / (kH * sH)) * rT
                      )
         );
     }

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