我想设计一个程序,可以帮助我在5种预定义的颜色中评估哪一种更类似于可变颜色,以及与可变颜色的百分比。问题是我不知道如何手动一步一步地做到这一点。所以想一个程序就更难了。

更多细节:颜色来自不同颜色的管子和凝胶的照片。我有5个不同颜色的管子,每个代表5个等级中的1个。我想拍摄其他样本的照片,然后在电脑上通过比较颜色来评估样本属于哪个级别,我也想知道一个近似的百分比。我想要一个这样做的程序:http://www.colortools.net/color_matcher.html

如果你能告诉我该采取什么步骤,即使它们需要我手动思考和执行。那会很有帮助的。


当前回答

以下所有方法的结果都是0-100。

internal static class ColorDifference
{
    internal enum Method
    {
        Binary, // true or false, 0 is false
        Square,
        Dimensional,
        CIE76
    }

    public static double Calculate(Method method, int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate(method, c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double Calculate(Method method, int r1, int r2, int g1, int g2, int b1, int b2, int a1 = -1, int a2 = -1)
    {
        switch (method)
        {
            case Method.Binary:
                return (r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2) ? 0 : 100;
            case Method.CIE76:
                return CalculateCIE76(r1, r2, g1, g2, b1, b2);
            case Method.Dimensional:
                if (a1 == -1 || a2 == -1) return Calculate3D(r1, r2, g1, g2, b1, b2);
                else return Calculate4D(r1, r2, g1, g2, b1, b2, a1, a2);
            case Method.Square:
                return CalculateSquare(r1, r2, g1, g2, b1, b2, a1, a2);
            default:
                throw new InvalidOperationException();
        }
    }

    public static double Calculate(Method method, Color c1, Color c2, bool alpha)
    {
        switch (method)
        {
            case Method.Binary:
                return (c1.R == c2.R && c1.G == c2.G && c1.B == c2.B && (!alpha || c1.A == c2.A)) ? 0 : 100;
            case Method.CIE76:
                if (alpha) throw new InvalidOperationException();
                return CalculateCIE76(c1, c2);
            case Method.Dimensional:
                if (alpha) return Calculate4D(c1, c2);
                else return Calculate3D(c1, c2);
            case Method.Square:
                if (alpha) return CalculateSquareAlpha(c1, c2);
                else return CalculateSquare(c1, c2);
            default:
                throw new InvalidOperationException();
        }
    }

    // A simple idea, based on on a Square
    public static double CalculateSquare(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return CalculateSquare(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3]);
    }

    public static double CalculateSquare(Color c1, Color c2)
    {
        return CalculateSquare(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double CalculateSquareAlpha(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return CalculateSquare(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double CalculateSquareAlpha(Color c1, Color c2)
    {
        return CalculateSquare(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B, c1.A, c2.A);
    }

    public static double CalculateSquare(int r1, int r2, int g1, int g2, int b1, int b2, int a1 = -1, int a2 = -1)
    {
        if (a1 == -1 || a2 == -1) return (Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2)) / 7.65;
        else return (Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2) + Math.Abs(a1 - a2)) / 10.2;
    }

    // from:http://stackoverflow.com/questions/9018016/how-to-compare-two-colors
    public static double Calculate3D(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate3D(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3]);
    }

    public static double Calculate3D(Color c1, Color c2)
    {
        return Calculate3D(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double Calculate3D(int r1, int r2, int g1, int g2, int b1, int b2)
    {
        return Math.Sqrt(Math.Pow(Math.Abs(r1 - r2), 2) + Math.Pow(Math.Abs(g1 - g2), 2) + Math.Pow(Math.Abs(b1 - b2), 2)) / 4.41672955930063709849498817084;
    }

    // Same as above, but made 4D to include alpha channel
    public static double Calculate4D(int argb1, int argb2)
    {
        int[] c1 = ColorConversion.ArgbToArray(argb1);
        int[] c2 = ColorConversion.ArgbToArray(argb2);
        return Calculate4D(c1[1], c2[1], c1[2], c2[2], c1[3], c2[3], c1[0], c2[0]);
    }

    public static double Calculate4D(Color c1, Color c2)
    {
        return Calculate4D(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B, c1.A, c2.A);
    }

    public static double Calculate4D(int r1, int r2, int g1, int g2, int b1, int b2, int a1, int a2)
    {
        return Math.Sqrt(Math.Pow(Math.Abs(r1 - r2), 2) + Math.Pow(Math.Abs(g1 - g2), 2) + Math.Pow(Math.Abs(b1 - b2), 2) + Math.Pow(Math.Abs(a1 - a2), 2)) / 5.1;
    }

    /**
    * 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 CalculateCIE76(int argb1, int argb2)
    {
        return CalculateCIE76(Color.FromArgb(argb1), Color.FromArgb(argb2));
    }

    public static double CalculateCIE76(Color c1, Color c2)
    {
        return CalculateCIE76(c1.R, c2.R, c1.G, c2.G, c1.B, c2.B);
    }

    public static double CalculateCIE76(int r1, int r2, int g1, int g2, int b1, int b2)
    {
        int[] lab1 = ColorConversion.ColorToLab(r1, g1, b1);
        int[] lab2 = ColorConversion.ColorToLab(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)) / 2.55;
    }
}


internal static class ColorConversion
{

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

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

        double r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
        double Ls, fas, fbs;
        double eps = 216.0f / 24389.0f;
        double k = 24389.0f / 27.0f;

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

        // RGB to XYZ
        r = R / 255.0f; //R 0..1
        g = G / 255.0f; //G 0..1
        b = B / 255.0f; //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.0);
        else fx = (float)((k * xr + 16.0) / 116.0);

        if (yr > eps) fy = (float)Math.Pow(yr, 1 / 3.0);
        else fy = (float)((k * yr + 16.0) / 116.0);

        if (zr > eps) fz = (float)Math.Pow(zr, 1 / 3.0);
        else fz = (float)((k * zr + 16.0) / 116);

        Ls = (116 * fy) - 16;
        fas = 500 * (fx - fy);
        fbs = 200 * (fy - fz);

        int[] lab = new int[3];
        lab[0] = (int)(2.55 * Ls + 0.5);
        lab[1] = (int)(fas + 0.5);
        lab[2] = (int)(fbs + 0.5);
        return lab;
    }
}

其他回答

我尝试了各种方法,如LAB颜色空间,HSV比较,我发现光度在这个目的上非常有效。

这是Python版本

def lum(c):
    def factor(component):
        component = component / 255;
        if (component <= 0.03928):
            component = component / 12.92;
        else:
            component = math.pow(((component + 0.055) / 1.055), 2.4);

        return component
    components = [factor(ci) for ci in c]

    return (components[0] * 0.2126 + components[1] * 0.7152 + components[2] * 0.0722) + 0.05;

def color_distance(c1, c2):

    l1 = lum(c1)
    l2 = lum(c2)
    higher = max(l1, l2)
    lower = min(l1, l2)

    return (higher - lower) / higher


c1 = ImageColor.getrgb('white')
c2 = ImageColor.getrgb('yellow')
print(color_distance(c1, c2))

会给你

0.0687619047619048

一个只使用RGB的简单方法是

cR=R1-R2 
cG=G1-G2 
cB=B1-B2 
uR=R1+R2 
distance=cR*cR*(2+uR/256) + cG*cG*4 + cB*cB*(2+(255-uR)/256)

我已经使用这个工具有一段时间了,它可以很好地用于大多数目的。

这只是我第一次想到的一个想法(如果愚蠢的话,对不起)。 颜色的三个分量可以假定为点的三维坐标,然后可以计算点之间的距离。

外汇期货

Point1 has R1 G1 B1
Point2 has R2 G2 B2

颜色之间的距离为

d=sqrt((r2-r1)^2+(g2-g1)^2+(b2-b1)^2)

比例是

p=d/sqrt((255)^2+(255)^2+(255)^2)

我在我的android up中使用了这个,它似乎令人满意,尽管不建议使用RGB空间:

    public double colourDistance(int red1,int green1, int blue1, int red2, int green2, int blue2)
{
      double rmean = ( red1 + red2 )/2;
    int r = red1 - red2;
    int g = green1 - green2;
    int b = blue1 - blue2;
    double weightR = 2 + rmean/256;
    double weightG = 4.0;
    double weightB = 2 + (255-rmean)/256;
    return Math.sqrt(weightR*r*r + weightG*g*g + weightB*b*b);
}

然后我用下面的方法得到相似度的百分比:

double maxColDist = 764.8339663572415;
double d1 = colourDistance(red1,green1,blue1,red2,green2,blue2);
String s1 = (int) Math.round(((maxColDist-d1)/maxColDist)*100) + "% match";

它工作得很好。

Android for ColorUtils API RGBToHSL 我有两个int argb颜色(color1, color2),我想要得到两种颜色之间的距离/差异。这是我所做的;

private float getHue(int color) {
    int R = (color >> 16) & 0xff;
    int G = (color >>  8) & 0xff;
    int B = (color      ) & 0xff;
    float[] colorHue = new float[3];
    ColorUtils.RGBToHSL(R, G, B, colorHue);
    return colorHue[0];
}

然后我使用下面的代码来查找两种颜色之间的距离。

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}