如何比较两种颜色的相似性/差异性

Ana*_*des 161 algorithm rgb compare colors hsv

我想设计一个程序,可以帮助我评估5种预定义的颜色,其中一种颜色更接近可变颜色,以及百分比.问题是我不知道如何逐步手动完成.所以想到一个程序就更难了.

更多细节:颜色来自具有不同颜色的凝胶管的照片.我有5个不同颜色的管子,每个都代表5个级别中的1个.我想拍摄其他样品的照片,并在计算机上通过比较颜色来评估样品属于哪个级别,我想知道这也是近似的百分比.我想要一个类似这样的程序:http://www.colortools.net/color_matcher.html

如果你能告诉我采取什么步骤,即使它们是让我思考和手动做的事情.这将非常有帮助.

Liu*_*kys 127

有关正确的潜在客户,请参阅Wikipedia关于色差的文章.基本上,您希望在某些多维颜色空间中计算距离度量.但RGB不是"感知统一",因此Vadim建议的欧几里德RGB距离度量与人类感知的颜色距离不匹配.首先,L a b*旨在是感知上均匀的颜色空间,并且通常使用deltaE度量.但是有更多精致的色彩空间和更精确的deltaE公式,更接近人类的感知.

您需要了解有关色彩空间和光源的更多信息才能进行转换.但是对于比欧几里德RGB度量更好的快速公式,只需执行以下操作:假设您的RGB值在sRGB颜色空间中,找到sRGB到L a b*转换公式,将sRGB颜色转换为L a b*,并计算两个L a b*值之间的deltaE .它的计算成本并不昂贵,只是一些非线性公式和一些乘法和加法.

  • "deltaE"为+1,这是最标准化的比较方法,并且针对不同的用例有deltaE公式的修改. (9认同)
  • 您可以在此处找到转换公式:http://www.brucelindbloom.com/index.html?Equations.html (9认同)
  • 或者,如果您在Ruby中工作,请查看实现[deltaE]的[`color` gem](https://rubygems.org/gems/color)(https://github.com/halostatue/color/ blob/259f58c1a54c7d2c86f3a227b7dced6235c69cdd/lib/color/rgb.rb#L382)以及其他颜色操作. (4认同)
  • 以下是上述 Javascript 实现的要点 https://gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152 (4认同)
  • +1.[L*U*V*](http://en.wikipedia.org/wiki/CIELUV)给出了非常好的结果 (2认同)

Vad*_*kin 43

只是一个想法,首先出现在我的脑海里(对不起,如果愚蠢).颜色的三个分量可以假设为点的3D坐标,然后您可以计算点之间的距离.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2
Run Code Online (Sandbox Code Playgroud)

颜色之间的距离是

d=sqrt((r2-r1)^2+(g2-g1)^2+(b2-b1)^2)
Run Code Online (Sandbox Code Playgroud)

百分比是

p=d/sqrt((255)^2+(255)^2+(255)^2)
Run Code Online (Sandbox Code Playgroud)

  • 如果我们使用RGB色彩空间,2种颜色之间的差异与人类*感知*差异的方式不同.但是,基本的想法在任何地方都是一样的 - 我们只需将它映射到另一个颜色空间(实验室我认为) (27认同)
  • 这里的另一个问题是255,0,0与0,255,0的距离相同,因为它是0,0,255. (9认同)
  • @Voo:我同意,对于基于距离的相似性匹配,HSV/HSL/LAB将比(s)RGB显着更好的色彩空间. (6认同)
  • 这是告诉你两种颜色有多不同的好方法,但是告诉你它们与感知有多么不同.人眼远非完美:我们对绿色比对红色或蓝色更敏感,我们的亮度感知是对数等等.OP从未指定他/她想要的东西; 但是[见这里](http://stackoverflow.com/questions/2103368)是一种专门为人类视觉定制的算法. (4认同)

Sup*_*upr 23

颜色值具有多个维度,因此没有内在的方法来比较两种颜色.您必须根据用例确定颜色的含义,以及如何最好地比较它们.

您很可能想要将颜色的色调,饱和度和/或亮度属性与红色/绿色/蓝色组件进行比较.如果你在弄清楚如何比较它们时遇到困难,可以采取一些样本颜色并在心理上进行比较,然后尝试证明/解释为什么它们相似/不同.

一旦知道要比较颜色的哪些属性/组件,就需要弄清楚如何从颜色中提取该信息.

很可能你只需要将颜色从常见的RedGreenBlue表示转换为HueSaturationLightness,然后计算像

avghue = (color1.hue + color2.hue)/2
distance = abs(color1.hue-avghue)
Run Code Online (Sandbox Code Playgroud)

此示例将为您提供一个简单的标量值,指示颜色的渐变/色调彼此之间的距离.

请参阅维基百科的HSL和HSV.

  • 从我记得的关于这些事情的讲座中,我会将图像转换为Lab颜色空间而不是HSV/HSL.挑选那个的任何理由? (2认同)

alo*_*ser 21

实际上我几个月前走过了同样的道路.这个问题没有完美的答案(这里有几次问)但是有一个更复杂的sqrt(rr)等答案,更容易直接用RGB实现,而不会移动到所有类型的替代色彩空间.我在这里找到了这个公式,这是一个相当复杂的实际公式的低成本近似 (通过CIE,它是W3C的颜色,因为这是一个未完成的任务,你可以在那里找到更老和更简单的色差方程).祝好运

编辑:对于后代,这是相关的C代码:

typedef struct {
     unsigned char r, g, b;
} RGB;

double ColourDistance(RGB e1, RGB 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 sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8));
}
Run Code Online (Sandbox Code Playgroud)


kba*_*kba 20

如果你有两个Color对象c1c2,用户可于比较每个RGB值c1与的c2.

int diffRed   = Math.abs(c1.getRed()   - c2.getRed());
int diffGreen = Math.abs(c1.getGreen() - c2.getGreen());
int diffBlue  = Math.abs(c1.getBlue()  - c2.getBlue());
Run Code Online (Sandbox Code Playgroud)

那些值你可以除以差值饱和度(255),你将得到两者之间的差异.

float pctDiffRed   = (float)diffRed   / 255;
float pctDiffGreen = (float)diffGreen / 255;
float pctDiffBlue   = (float)diffBlue  / 255;
Run Code Online (Sandbox Code Playgroud)

之后,您可以找到百分比的平均色差.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100
Run Code Online (Sandbox Code Playgroud)

这将使你在之间百分比差c1c2.

  • 这可能不会给出最佳的"可见"差异,因为人眼会不同地感知颜色变化.话虽这么说,我猜这正是她正在寻找的,因为她可能正在寻找一个同样可量化的差异,而不是一个被认可的差异.我以为我会把它作为一个需要考虑的事情,如果它是相关的. (17认同)

Ivo*_*nov 14

通过人类感知比较两种颜色的最佳方法之一是CIE76.差异称为Delta-E.当它小于1时,人眼无法识别出差异.

有很棒的颜色实用程序类ColorUtils(下面的代码),其中包括CIE76比较方法.它由苏黎世大学的Daniel Strebel撰写.

从ColorUtils.class我使用方法:

static double colorDifference(int r1, int g1, int b1, int r2, int g2, int b2)
Run Code Online (Sandbox Code Playgroud)

r1,g1,b1 - 第一种颜色的RGB值

r2,g2,b2 - RGB值或您想要比较的第二种颜色

如果您使用Android,则可以获得以下值:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


来自苏黎世大学Daniel Strebel的ColorUtils.class:

import android.graphics.Color;

public class ColorUtil {
public static int argb(int R, int G, int B) {
    return argb(Byte.MAX_VALUE, R, G, B);
}

public static int argb(int A, int R, int G, int B) {
    byte[] colorByteArr = {(byte) A, (byte) R, (byte) G, (byte) B};
    return byteArrToInt(colorByteArr);
}

public static int[] rgb(int argb) {
    return new int[]{(argb >> 16) & 0xFF, (argb >> 8) & 0xFF, argb & 0xFF};
}

public static int byteArrToInt(byte[] colorByteArr) {
    return (colorByteArr[0] << 24) + ((colorByteArr[1] & 0xFF) << 16)
            + ((colorByteArr[2] & 0xFF) << 8) + (colorByteArr[3] & 0xFF);
}

public static int[] rgb2lab(int R, int G, int B) {
    //http://www.brucelindbloom.com

    float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
    float Ls, as, bs;
    float eps = 216.f / 24389.f;
    float k = 24389.f / 27.f;

    float Xr = 0.964221f;  // reference white D50
    float Yr = 1.0f;
    float Zr = 0.825211f;

    // RGB to XYZ
    r = R / 255.f; //R 0..1
    g = G / 255.f; //G 0..1
    b = B / 255.f; //B 0..1

    // assuming sRGB (D65)
    if (r <= 0.04045)
        r = r / 12;
    else
        r = (float) Math.pow((r + 0.055) / 1.055, 2.4);

    if (g <= 0.04045)
        g = g / 12;
    else
        g = (float) Math.pow((g + 0.055) / 1.055, 2.4);

    if (b <= 0.04045)
        b = b / 12;
    else
        b = (float) Math.pow((b + 0.055) / 1.055, 2.4);


    X = 0.436052025f * r + 0.385081593f * g + 0.143087414f * b;
    Y = 0.222491598f * r + 0.71688606f * g + 0.060621486f * b;
    Z = 0.013929122f * r + 0.097097002f * g + 0.71418547f * b;

    // XYZ to Lab
    xr = X / Xr;
    yr = Y / Yr;
    zr = Z / Zr;

    if (xr > eps)
        fx = (float) Math.pow(xr, 1 / 3.);
    else
        fx = (float) ((k * xr + 16.) / 116.);

    if (yr > eps)
        fy = (float) Math.pow(yr, 1 / 3.);
    else
        fy = (float) ((k * yr + 16.) / 116.);

    if (zr > eps)
        fz = (float) Math.pow(zr, 1 / 3.);
    else
        fz = (float) ((k * zr + 16.) / 116);

    Ls = (116 * fy) - 16;
    as = 500 * (fx - fy);
    bs = 200 * (fy - fz);

    int[] lab = new int[3];
    lab[0] = (int) (2.55 * Ls + .5);
    lab[1] = (int) (as + .5);
    lab[2] = (int) (bs + .5);
    return lab;
}

/**
 * Computes the difference between two RGB colors by converting them to the L*a*b scale and
 * comparing them using the CIE76 algorithm { http://en.wikipedia.org/wiki/Color_difference#CIE76}
 */
public static double getColorDifference(int a, int b) {
    int r1, g1, b1, r2, g2, b2;
    r1 = Color.red(a);
    g1 = Color.green(a);
    b1 = Color.blue(a);
    r2 = Color.red(b);
    g2 = Color.green(b);
    b2 = Color.blue(b);
    int[] lab1 = rgb2lab(r1, g1, b1);
    int[] lab2 = rgb2lab(r2, g2, b2);
    return Math.sqrt(Math.pow(lab2[0] - lab1[0], 2) + Math.pow(lab2[1] - lab1[1], 2) + Math.pow(lab2[2] - lab1[2], 2));
}
}
Run Code Online (Sandbox Code Playgroud)


Voo*_*Voo 10

只是另一个答案,虽然它与Supr的相似 - 只是一个不同的色彩空间.

问题是:人类不均匀地感知颜色的差异,RGB颜色空间忽略了这一点.因此,如果您使用RGB颜色空间并计算两种颜色之间的欧氏距离,您可能会得到数学上绝对正确的差异,但与人类告诉您的不一致.

这可能不是问题 - 我认为差异并不大,但如果你想解决这个"更好",你应该将你的RGB颜色转换为专门设计的颜色空间,以避免上述问题.有几个,早期模型的改进(因为这是基于人类感知,我们需要根据实验数据来衡量"正确"值).还有的Lab色彩空间,我觉得这是最好的,虽然有点复杂,将其转换为.更简单的是CIE XYZ.

这是一个网站,列出了在不同颜色空间之间进行转换的公式,以便您可以进行一些实验.


Pan*_*tel 5

Kotlin 版本与您想要匹配的百分比。

带百分比可选参数的方法调用

isMatchingColor(intColor1, intColor2, 95) // should match color if 95% similar
Run Code Online (Sandbox Code Playgroud)

方法体

private fun isMatchingColor(intColor1: Int, intColor2: Int, percent: Int = 90): Boolean {
    val threadSold = 255 - (255 / 100f * percent)

    val diffAlpha = abs(Color.alpha(intColor1) - Color.alpha(intColor2))
    val diffRed = abs(Color.red(intColor1) - Color.red(intColor2))
    val diffGreen = abs(Color.green(intColor1) - Color.green(intColor2))
    val diffBlue = abs(Color.blue(intColor1) - Color.blue(intColor2))

    if (diffAlpha > threadSold) {
        return false
    }

    if (diffRed > threadSold) {
        return false
    }

    if (diffGreen > threadSold) {
        return false
    }

    if (diffBlue > threadSold) {
        return false
    }

    return true
}
Run Code Online (Sandbox Code Playgroud)