Cómo comparar dos colores para similitud / diferencia

171

Quiero diseñar un programa que pueda ayudarme a evaluar entre 5 colores predefinidos cuál es más similar a un color variable y con qué porcentaje. El caso es que no sé cómo hacerlo manualmente paso a paso. Por lo tanto, es aún más difícil pensar en un programa.

Más detalles: Los colores son de fotografías de tubos con gel que en diferentes colores. Tengo 5 tubos con diferentes colores, cada uno representa 1 de 5 niveles. Quiero tomar fotografías de otras muestras y, en la computadora, evaluar a qué nivel pertenece esa muestra comparando colores, y también quiero saberlo con un porcentaje de aproximación. Me gustaría un programa que haga algo como esto: http://www.colortools.net/color_matcher.html

Si me puede decir qué pasos tomar, incluso si son cosas que debo pensar y hacer manualmente. Sería muy útil.

Ana Fernandes
fuente
1
Hice un pequeño cambio en el texto, cambiando una palabra portuguesa a lo que creo que es el equivalente correcto en inglés ... cámbielo de nuevo si cometí un error.
Beska
13
Hay un artículo de Wikipedia sobre la diferencia de color: en.wikipedia.org/wiki/Color_difference
Ocaso Protal
44
Esto debería ser interesante: stevehanov.ca/blog/index.php?id=116 Explora la computación de la diferencia en tres modelos de color diferentes.
Vlad
Hola @OcasoProtal, es un excelente enlace, gracias por compartir. Y para el OP, pregunta interesante.
Percepción el
Intente minimizar cualquier variabilidad fotográfica potencial también ... más detalles en la respuesta a continuación.
Beska

Respuestas:

130

Consulte el artículo de Wikipedia sobre la diferencia de color para conocer las pistas correctas. Básicamente, desea calcular una métrica de distancia en algún espacio de color multidimensional. Pero RGB no es "perceptualmente uniforme", por lo que su métrica de distancia Euclidiana RGB sugerida por Vadim no coincidirá con la distancia percibida por los humanos entre los colores. Para empezar, L a b * está destinado a ser un espacio de color perceptualmente uniforme, y la métrica deltaE se usa comúnmente. Pero hay espacios de colores más refinados y fórmulas deltaE más refinadas que se acercan a la percepción humana.

Tendrás que aprender más sobre espacios de colores e iluminantes para hacer las conversiones. Pero para una fórmula rápida que sea mejor que la métrica Euclidean RGB, solo haga esto: suponga que sus valores RGB están en el espacio de color sRGB, encuentre las fórmulas de conversión sRGB a L a b *, convierta sus colores sRGB a L a b *, y calcule deltaE entre sus dos valores L a b *. No es computacionalmente costoso, son solo algunas fórmulas no lineales y algunas multiplicaciones y sumas.

Liudvikas Bukys
fuente
11
+1 para "deltaE", ese es el método de comparación más estandarizado y hay adaptaciones de la fórmula deltaE para diferentes casos de uso.
Martin Hennings
9
Puede encontrar las fórmulas de conversión aquí: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez
44
O, si está trabajando en Ruby, eche un vistazo a la colorgema que implementa deltaE entre otras operaciones de color.
Mike Jarema
Aquí hay un resumen de la implementación anterior en Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C
46

Solo una idea que primero vino a mi mente (lo siento si es estúpida). Se pueden suponer tres componentes de colores coordenadas 3D de puntos y luego se puede calcular la distancia entre puntos.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

La distancia entre colores es

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

El porcentaje es

p=d/sqrt((255)^2+(255)^2+(255)^2)
Vadim Gulyakin
fuente
28
Sin embargo, si estamos utilizando el espacio de color RGB, la diferencia entre 2 colores no es la misma que la forma en que los humanos perciben la diferencia. Pero sí, la idea básica es la misma en todas partes: solo tendríamos que asignarla a otro espacio de color (laboratorio, creo)
Voo
66
@Voo: Estoy de acuerdo, HSV / HSL / LAB serían espacios de color significativamente mejores que RGB para la coincidencia de similitud basada en la distancia.
Jon Purdy
44
Esta es una buena manera de decirle cuán diferentes son los dos colores, pero hace un mal trabajo al decirle cuán diferentes serán PERCIBIDOS. Los ojos humanos están lejos de ser perfectos: somos más sensibles al verde que al rojo o al azul, nuestra percepción del brillo es logrítmica, etc. OP nunca especificó qué desea; pero vea aquí un algoritmo especialmente diseñado para la vista humana.
BlueRaja - Danny Pflughoeft
+ Es mi primera idea también.
ST3
9
Otro problema aquí es 255, 0, 0 es la misma distancia de 0, 255, 0 que 0, 0, 255.
27

De hecho, caminé por el mismo camino hace un par de meses. no hay una respuesta perfecta a la pregunta (que se hizo aquí un par de veces), pero hay una respuesta más sofisticada que la sqrt (rr, etc.) y más fácil de implementar directamente con RGB sin moverse a todo tipo de espacios de color alternativos. Encontré esta fórmula aquí, que es una aproximación de bajo costo de la fórmula real bastante complicada (por el CIE, que es el W3C del color, ya que esta es una búsqueda no terminada, puede encontrar ecuaciones de diferencia de color más antiguas y simples allí). buena suerte

Editar: para la posteridad, aquí está el código C relevante:

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));
}
alonisser
fuente
Este método funcionó para mi . Me ayudó a encontrar el color más cercano de la lista de nombres de colores.
faisalbhagat
23

Un valor de color tiene más de una dimensión, por lo que no hay una forma intrínseca de comparar dos colores. Debe determinar para su caso de uso el significado de los colores y, por lo tanto, cómo compararlos mejor.

Lo más probable es que desee comparar las propiedades de matiz, saturación y / o luminosidad de los colores opuestos a los componentes rojo / verde / azul. Si tiene problemas para determinar cómo quiere compararlos, tome algunos pares de colores de muestra y compárelos mentalmente, luego intente justificarse / explicarse por qué son similares / diferentes.

Una vez que sepa qué propiedades / componentes de los colores desea comparar, deberá descubrir cómo extraer esa información de un color.

Lo más probable es que solo necesite convertir el color de la representación común de RedGreenBlue a HueSaturationLightness, y luego calcular algo como

avghue = (color1.hue + color2.hue)/2
distance = abs(color1.hue-avghue)

Este ejemplo le daría un valor escalar simple que indica qué tan lejos están el gradiente / tono de los colores entre sí.

Ver HSL y HSV en Wikipedia .

Supr
fuente
2
De las cosas que recuerdo de mis conferencias sobre estas cosas, convertiría la imagen en el espacio de color Lab y no en HSV / HSL. ¿Algún razonamiento para elegir ese?
Voo
No RGB y HSL son los que estoy más familiarizado, así que elegí HSL solo para subrayar la idea de que el RGB "predeterminado" no es la única opción, sino que realmente depende de la aplicación. Gracias por informarme sobre el espacio de color Lab.
Supr
1
De todos modos, le di +1 porque el principio básico aquí es la respuesta "correcta" (convertir en un espacio de color que maneje la diferencia percibida de manera uniforme y luego hacer la comparación). No estoy seguro de qué espacio sería el mejor: todos estos espacios de color diferentes son confusos como el infierno;)
Voo
21

Si tiene dos Colorobjetos c1y c2, simplemente puede comparar cada valor RGB c1con el de 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());

Esos valores se pueden dividir por la cantidad de saturación de diferencia (255), y obtendrá la diferencia entre los dos.

float pctDiffRed   = (float)diffRed   / 255;
float pctDiffGreen = (float)diffGreen / 255;
float pctDiffBlue   = (float)diffBlue  / 255;

Después de lo cual puede encontrar la diferencia de color promedio en porcentaje.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Lo que te daría una diferencia en porcentaje entre c1y c2.

kba
fuente
2 cosas menores más: <b> 1 </b> pctDiffRed = diffRed / 255;te dará 0 a menos que lances a un flotador en alguna parte. <b> 2 </b> Tendrá que multiplicar por 100 en algún lugar para obtener un porcentaje.
vaughandroid
18
Esto puede no dar la mejor diferencia "visible", ya que el ojo humano percibe los cambios de color de manera diferente. Dicho esto, supongo que esto es exactamente lo que está buscando, porque probablemente esté buscando una diferencia igualmente cuantificable en lugar de una diferencia percibida. Solo pensé que esto sería algo a tener en cuenta en caso de que sea relevante.
Beska
14

Uno de los mejores métodos para comparar dos colores por percepción humana es CIE76. La diferencia se llama Delta-E. Cuando es menor que 1, el ojo humano no puede reconocer la diferencia.

Existe una maravillosa clase de utilidades de color ColorUtils (código a continuación), que incluye métodos de comparación CIE76. Está escrito por Daniel Strebel, Universidad de Zurich.

De ColorUtils.class utilizo el método:

static double colorDifference(int r1, int g1, int b1, int r2, int g2, int b2)

r1, g1, b1: valores RGB del primer color

r2, g2, b2: valores RGB del segundo color que desea comparar

Si trabaja con Android, puede obtener estos valores de esta manera:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class por Daniel Strebel, Universidad de Zurich:

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));
}
}
Ivo Stoyanov
fuente
el código anterior tiene un error en rgb2lab: la división por 12 debe ser reemplazada por la división por 12.92 en la conversión de r, g y b. de lo contrario, la función no es continua en r = 0.04045
John Smith
10

Solo otra respuesta, aunque es similar a la de Supr: solo un espacio de color diferente.

La cuestión es: los humanos perciben la diferencia de color de manera no uniforme y el espacio de color RGB lo ignora. Como resultado, si usa el espacio de color RGB y solo calcula la distancia euclidiana entre 2 colores, puede obtener una diferencia que es matemáticamente absolutamente correcta, pero que no coincidiría con lo que los humanos le dirían.

Esto puede no ser un problema, creo que la diferencia no es tan grande, pero si desea resolver este "mejor", debe convertir sus colores RGB en un espacio de color diseñado específicamente para evitar el problema anterior. Hay varios, mejoras de modelos anteriores (dado que esto se basa en la percepción humana, necesitamos medir los valores "correctos" en base a datos experimentales). Existe el espacio de color Lab, que creo que sería el mejor, aunque un poco complicado para convertirlo. Más simple sería el CIE XYZ .

Aquí hay un sitio que enumera las fórmulas para convertir entre diferentes espacios de color para que pueda experimentar un poco.

Voo
fuente
3

Todos los métodos a continuación dan como resultado una escala de 0 a 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;
    }
}
Vozzie
fuente
2

La mejor manera es deltaE. DeltaE es un número que muestra la diferencia de los colores. Si deltae <1, entonces la diferencia no puede ser reconocida por los ojos humanos. Escribí un código en canvas y js para convertir rgb a lab y luego calcular delta e. En este ejemplo, el código reconoce píxeles que tienen un color diferente con un color base que guardé como LAB1. y luego, si es diferente, esos píxeles se vuelven rojos. Puede aumentar o reducir la sensibilidad de la diferencia de color al aumentar o disminuir el rango aceptable de delta e. En este ejemplo, asigné 10 para deltaE en la línea que escribí (deltae <= 10):

<script>   
  var constants = {
    canvasWidth: 700, // In pixels.
    canvasHeight: 600, // In pixels.
    colorMap: new Array() 
          };



  // -----------------------------------------------------------------------------------------------------

  function fillcolormap(imageObj1) {


    function rgbtoxyz(red1,green1,blue1){ // a converter for converting rgb model to xyz model
 var red2 = red1/255;
 var green2 = green1/255;
 var blue2 = blue1/255;
 if(red2>0.04045){
      red2 = (red2+0.055)/1.055;
      red2 = Math.pow(red2,2.4);
 }
 else{
      red2 = red2/12.92;
 }
 if(green2>0.04045){
      green2 = (green2+0.055)/1.055;
      green2 = Math.pow(green2,2.4);    
 }
 else{
      green2 = green2/12.92;
 }
 if(blue2>0.04045){
      blue2 = (blue2+0.055)/1.055;
      blue2 = Math.pow(blue2,2.4);    
 }
 else{
      blue2 = blue2/12.92;
 }
 red2 = (red2*100);
 green2 = (green2*100);
 blue2 = (blue2*100);
 var x = (red2 * 0.4124) + (green2 * 0.3576) + (blue2 * 0.1805);
 var y = (red2 * 0.2126) + (green2 * 0.7152) + (blue2 * 0.0722);
 var z = (red2 * 0.0193) + (green2 * 0.1192) + (blue2 * 0.9505);
 var xyzresult = new Array();
 xyzresult[0] = x;
 xyzresult[1] = y;
 xyzresult[2] = z;
 return(xyzresult);
} //end of rgb_to_xyz function
function xyztolab(xyz){ //a convertor from xyz to lab model
 var x = xyz[0];
 var y = xyz[1];
 var z = xyz[2];
 var x2 = x/95.047;
 var y2 = y/100;
 var z2 = z/108.883;
 if(x2>0.008856){
      x2 = Math.pow(x2,1/3);
 }
 else{
      x2 = (7.787*x2) + (16/116);
 }
 if(y2>0.008856){
      y2 = Math.pow(y2,1/3);
 }
 else{
      y2 = (7.787*y2) + (16/116);
 }
 if(z2>0.008856){
      z2 = Math.pow(z2,1/3);
 }
 else{
      z2 = (7.787*z2) + (16/116);
 }
 var l= 116*y2 - 16;
 var a= 500*(x2-y2);
 var b= 200*(y2-z2);
 var labresult = new Array();
 labresult[0] = l;
 labresult[1] = a;
 labresult[2] = b;
 return(labresult);

}

    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');
    var imageX = 0;
    var imageY = 0;

    context.drawImage(imageObj1, imageX, imageY, 240, 140);
    var imageData = context.getImageData(0, 0, 240, 140);
    var data = imageData.data;
    var n = data.length;
   // iterate over all pixels

    var m = 0;
    for (var i = 0; i < n; i += 4) {
      var red = data[i];
      var green = data[i + 1];
      var blue = data[i + 2];
    var xyzcolor = new Array();
    xyzcolor = rgbtoxyz(red,green,blue);
    var lab = new Array();
    lab = xyztolab(xyzcolor);
    constants.colorMap.push(lab); //fill up the colormap array with lab colors.         
      } 

  }

// ------------------------------------------------ -------------------------------------------------- ---

    function colorize(pixqty) {

         function deltae94(lab1,lab2){    //calculating Delta E 1994

         var c1 = Math.sqrt((lab1[1]*lab1[1])+(lab1[2]*lab1[2]));
         var c2 =  Math.sqrt((lab2[1]*lab2[1])+(lab2[2]*lab2[2]));
         var dc = c1-c2;
         var dl = lab1[0]-lab2[0];
         var da = lab1[1]-lab2[1];
         var db = lab1[2]-lab2[2];
         var dh = Math.sqrt((da*da)+(db*db)-(dc*dc));
         var first = dl;
         var second = dc/(1+(0.045*c1));
         var third = dh/(1+(0.015*c1));
         var deresult = Math.sqrt((first*first)+(second*second)+(third*third));
         return(deresult);
          } // end of deltae94 function
    var lab11 =  new Array("80","-4","21");
    var lab12 = new Array();
    var k2=0;
    var canvas = document.getElementById('myCanvas');
                                        var context = canvas.getContext('2d');
                                        var imageData = context.getImageData(0, 0, 240, 140);
                                        var data = imageData.data;

    for (var i=0; i<pixqty; i++) {

    lab12 = constants.colorMap[i];

    var deltae = deltae94(lab11,lab12);     
                                        if (deltae <= 10) {

                                        data[i*4] = 255;
                                        data[(i*4)+1] = 0;
                                        data[(i*4)+2] = 0;  
                                        k2++;
                                        } // end of if 
                                } //end of for loop
    context.clearRect(0,0,240,140);
    alert(k2);
    context.putImageData(imageData,0,0);
} 
// -----------------------------------------------------------------------------------------------------

$(window).load(function () {    
  var imageObj = new Image();
  imageObj.onload = function() {
  fillcolormap(imageObj);    
  }
  imageObj.src = './mixcolor.png';
});

// ---------------------------------------------------------------------------------------------------
 var pixno2 = 240*140; 
 </script>
Iman Sedighi
fuente
1
Estoy un poco preocupado por algunas de tus divisiones enteras. 1/3y 16/116ambos evalúan 0, lo que casi seguro no es lo que quieres. Probablemente su algoritmo sea correcto, pero su código ciertamente no lo es.
Dawood ibn Kareem
Estás describiendo CIE-LAB dE94. Delta E significa el cambio en Euclidiana. Es decir, en el espacio de color Lab estándar, la distancia euclidiana dada por su fórmula de distancia euclidiana muy estándar. Mientras que las modificaciones del Delta E, a saber, 76, 94, 2000 (también hay Delta E, CMC que se usa para textiles y similares) son fórmulas de distancia diferentes entre posiciones dentro del espacio de color Lab. El código para el laboratorio es el mismo en cada uno, el código para la diferencia de color no lo es. . En resumen, Delta E, no es lo que se llama.
Tatarizar
2

Un método simple que solo usa RGB es

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)

He usado este por un tiempo ahora, y funciona lo suficientemente bien para la mayoría de los propósitos.

Bob Pickle
fuente
Usando la fórmula anterior, ¿cuál es el rango de valores para la distancia?
Aman Aggarwal
Esto está bastante cerca de la aproximación de diferencia de color euclidiana. Supongo que está omitiendo el componente raíz para acelerar el cálculo, por lo que es un rango de 0 a 100 ^ 3. Si quieres normalizar a 100, haz distancia al poder de1/3
Daniel
2

Utilicé esto en mi Android y parece satisfactorio, aunque no se recomienda el espacio 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);
}

Luego usé lo siguiente para obtener el porcentaje de similitud:

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

Funciona bastante bien.

SimpleCoder
fuente
2

He probado varios métodos como el espacio de color LAB, las comparaciones de HSV y he descubierto que la luminosidad funciona bastante bien para este propósito.

Aquí está la versión de 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))

Te regalaré

0.0687619047619048
Tadas Šubonis
fuente
¿Cuál es el origen de ImageColor? editar Encontré, esfrom PIL import ImageColor
ademar111190
¿No es la luminosidad el brillo de un color? Entonces, en este caso, no se informará que un color verde, azul y rojo sea diferente siempre que el brillo sea el mismo.
Peter B.
1

Espero que quieras analizar una imagen completa al final, ¿no? Entonces puede verificar la diferencia más pequeña / más alta en la matriz de color de identidad.

La mayoría de las operaciones matemáticas para procesar gráficos usan matrices, porque los posibles algoritmos que las usan son a menudo más rápidos que los cálculos clásicos de distancia por punto y de comparación. (por ejemplo, para operaciones con DirectX, OpenGL, ...)

Así que creo que deberías comenzar aquí:

http://en.wikipedia.org/wiki/Identity_matrix

http://en.wikipedia.org/wiki/Matrix_difference_equation

... y como Beska ya comentó anteriormente:

Esto puede no dar la mejor diferencia "visible" ...

Lo que significa también que su algoritmo depende de su definición de "similar a" si está procesando imágenes.

Beachwalker
fuente
1

Versión de Kotlin con cuánto porcentaje quieres igualar.

Llamada de método con argumento opcional porcentual

isMatchingColor(intColor1, intColor2, 95) // should match color if 95% similar

Cuerpo del método

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
}
Pankaj Kant Patel
fuente
0

Deberá convertir los colores RGB en el espacio de color Lab para poder compararlos de la forma en que los humanos los ven. De lo contrario, obtendrá colores RGB que 'coinciden' de maneras muy extrañas.

El enlace de Wikipedia en Color Differences le ofrece una introducción a los diversos algoritmos de diferencia de espacio de color de Lab que se han definido a lo largo de los años. El más simple que solo verifica la distancia euclidiana de dos colores de laboratorio, funciona pero tiene algunos defectos.

Convenientemente, hay una implementación de Java del algoritmo CIEDE2000 más sofisticado en el proyecto OpenIMAJ . Proporcione sus dos conjuntos de colores Lab y le devolverá un valor de distancia única.

AiTuDou
fuente
0

La única forma "correcta" de comparar colores es hacerlo con deltaE en CIELab o CIELuv.

Pero para muchas aplicaciones, creo que esta es una aproximación lo suficientemente buena:

distance = 3 * |dR| + 4 * |dG| + 3 * |dB|

Creo que una distancia ponderada de Manhattan tiene mucho más sentido al comparar colores. Recuerda que los colores primarios solo están en nuestra cabeza. No tienen ningún significado físico. CIELab y CIELuv se modelan estadísticamente a partir de nuestra percepción del color.

onemasse
fuente
0

Para rápido y sucio, puedes hacer

import java.awt.Color;
private Color dropPrecision(Color c,int threshold){
    return new Color((c.getRed()/threshold),
                     (c.getGreen()/threshold),
                     (c.getBlue()/threshold));
}
public boolean inThreshold(Color _1,Color _2,int threshold){
    return dropPrecision(_1,threshold)==dropPrecision(_2,threshold);
}

haciendo uso de la división entera para cuantizar los colores.

Austin_Anderson
fuente
0

Respuesta rápida 5

Encontré este hilo porque necesitaba una versión rápida de esta pregunta. Como nadie ha respondido con la solución, aquí está el mío:

extension UIColor {

    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)

        return (red, green, blue, alpha)
    }

    func isSimilar(to colorB: UIColor) -> Bool {
        let rgbA = self.rgba
        let rgbB = colorB.rgba

        let diffRed = abs(CGFloat(rgbA.red) - CGFloat(rgbB.red))
        let diffGreen = abs(rgbA.green - rgbB.green)
        let diffBlue = abs(rgbA.blue - rgbB.blue)

        let pctRed = diffRed
        let pctGreen = diffGreen
        let pctBlue = diffBlue

        let pct = (pctRed + pctGreen + pctBlue) / 3 * 100

        return pct < 10 ? true : false
    }
}

Uso:

let black: UIColor = UIColor.black
let white: UIColor = UIColor.white

let similar: Bool = black.isSimilar(to: white)

Configuré menos del 10% de diferencia para devolver colores similares, pero puedes personalizarlo tú mismo.

VSN
fuente
0

Android para ColorUtils API RGBToHSL: Tenía dos colores int argb (color1, color2) y quería obtener la distancia / diferencia entre los dos colores. Aquí esta lo que hice;

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];
}

Luego usé el siguiente código para encontrar la distancia entre los dos colores.

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