Como comparar duas cores para semelhança / diferença


171

Quero criar um programa que possa me ajudar a avaliar entre 5 cores predefinidas, qual é mais semelhante a uma cor variável e com qual porcentagem. O fato é que eu não sei como fazer isso manualmente passo a passo. Portanto, é ainda mais difícil pensar em um programa.

Mais detalhes: As cores são de fotografias de tubos com gel que são de cores diferentes. Eu tenho 5 tubos com cores diferentes, cada um é representativo de 1 de 5 níveis. Quero tirar fotografias de outras amostras e, no computador, avaliar em que nível essa amostra pertence comparando cores, e também quero saber isso com uma porcentagem de aproximação. Eu gostaria de um programa que faça algo assim: http://www.colortools.net/color_matcher.html

Se você pode me dizer que medidas tomar, mesmo que sejam coisas para eu pensar e fazer manualmente. Seria muito útil.


1
Fiz uma pequena alteração no texto, alterando uma palavra em português para o que eu acho que é o equivalente em inglês correto ... troque de volta se eu errar.
Beska

13
Existe um artigo na Wikipedia sobre diferença de cores: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Isso deve ser interessante: stevehanov.ca/blog/index.php?id=116 Explora a computação da diferença em três modelos de cores diferentes.
Vlad

Olá @OcasoProtal, esse é um ótimo link, obrigado por compartilhar. E para o OP, pergunta interessante.
Percepção

Tente minimizar qualquer potencial variabilidade fotográfica também ... mais detalhes na resposta abaixo.
Beska

Respostas:


130

Consulte o artigo da Wikipedia sobre Diferença de cores para obter as pistas certas. Basicamente, você deseja calcular uma métrica de distância em algum espaço de cores multidimensional. Como o RGB não é "perceptualmente uniforme", sua métrica de distância RGB euclidiana sugerida por Vadim não corresponderá à distância percebida pelo homem entre as cores. Para começar, L a b * pretende ser um espaço de cores perceptualmente uniforme, e a métrica deltaE é comumente usada. Mas existem espaços de cores mais refinados e fórmulas delta mais refinadas que se aproximam da correspondência da percepção humana.

Você precisará aprender mais sobre espaços de cores e iluminantes para fazer as conversões. Mas, para uma fórmula rápida melhor que a métrica RGB euclidiana, faça o seguinte: assuma que seus valores RGB estão no espaço de cores sRGB, encontre as fórmulas de conversão de sRGB em L a b *, converta suas cores sRGB em L a b *, e calcule o deltaE entre seus dois valores L a b *. Não é computacionalmente caro, são apenas algumas fórmulas não lineares e algumas multiplicam e adicionam.


11
+1 para "deltaE", esse é o método de comparação mais padronizado e há adaptações da fórmula deltaE para diferentes casos de uso.
Martin Hennings

9
Você pode encontrar as fórmulas de conversão aqui: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Ou, se você estiver trabalhando em Ruby, confira a colorgema que implementa o deltaE entre outras operações de cores.
Mike Jarema

Aqui está uma síntese da
Ryan.C

46

Apenas uma idéia que me veio à mente (desculpe se for estúpido). Três componentes de cores podem ser assumidas coordenadas 3D de pontos e, em seguida, você pode calcular a distância entre os pontos.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

A distância entre cores é

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

Porcentagem é

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

28
Se estivermos usando o espaço de cores RGB, a diferença entre duas cores não é a mesma que a maneira como os humanos percebem a diferença. Mas sim a idéia básica é a mesma em todos os lugares - nós apenas temos que mapeá-lo em outro espaço de cor (laboratório eu acho)
Voo

6
@Voo: Eu concordo, HSV / HSL / LAB seria significativamente melhores espaços de cor do que RGB para correspondência de similaridade com base na distância.
precisa saber é o seguinte

4
Essa é uma boa maneira de dizer o quão diferentes são as duas cores, mas faz um mau trabalho em dizer o quão diferentes elas serão PERCEBIDAS. Os olhos humanos estão longe de serem perfeitos: somos mais sensíveis ao verde do que ao vermelho ou ao azul, nossa percepção do brilho é logrítmica, etc. OP nunca especificou o que ele / ela deseja; mas veja aqui um algoritmo especialmente adaptado para visão humana.
BlueRaja # Danny Pflughoeft

+ É a minha primeira ideia também.
ST3 30/06

9
Outro problema aqui é 255, 0, 0 é a mesma distância de 0, 255, 0, pois é 0, 0, 255.

27

na verdade, eu segui o mesmo caminho alguns meses atrás. não há resposta perfeita para a pergunta (que foi feita aqui algumas vezes), mas há uma mais sofisticada do que a resposta sqrt (rr) etc. e mais fácil de implantar diretamente com RGB sem mover para todos os tipos de espaços de cores alternativos. Encontrei essa fórmula aqui, que é uma aproximação de baixo custo da fórmula real bastante complicada (pela CIE, que é o W3C da cor, já que essa é uma busca ainda não concluída, você pode encontrar equações de diferença de cor mais antigas e simples). boa sorte

Edit: Para posteridade, aqui está o 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));
}

esse método funcionou para mim. Isso me ajudou a encontrar a cor mais próxima da lista de nomes de cores.
faisalbhagat

23

Um valor de cor possui mais de uma dimensão, portanto, não há uma maneira intrínseca de comparar duas cores. Você precisa determinar para o seu caso de uso o significado das cores e, assim, a melhor forma de compará-las.

Provavelmente, você deseja comparar as propriedades de matiz, saturação e / ou luminosidade das cores, em oposição aos componentes vermelho / verde / azul. Se você está tendo problemas para descobrir como deseja compará-las, pegue alguns pares de cores de amostra e compare-as mentalmente, depois tente justificar / explicar por que elas são semelhantes / diferentes.

Depois de saber quais propriedades / componentes das cores você deseja comparar, você precisará descobrir como extrair essas informações de uma cor.

Provavelmente, você precisará converter a cor da representação comum RedGreenBlue em HueSaturationLightness e calcular algo como

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

Este exemplo forneceria um valor escalar simples, indicando a que distância o gradiente / matiz das cores está um do outro.

Veja HSL e HSV na Wikipedia .


2
Pelo material que lembro de minhas palestras sobre essas coisas, eu converteria a imagem no espaço de cores do laboratório e não no HSV / HSL. Algum raciocínio para escolher esse?
Voo 26/01

Não. Estou familiarizado com RGB e HSL, então escolhi HSL apenas para enfatizar a ideia de que o RGB "padrão" não é a única opção - depende realmente do aplicativo. Obrigado por me informar sobre o espaço de cores do laboratório.
Supr

1
Dei-lhe +1 de qualquer maneira, porque o princípio básico aqui é a resposta "certa" (converta no espaço de cores que lida com a diferença percebida uniformemente e faça a comparação). Eu não tenho certeza de qual espaço seria o melhor - todos esses espaços de cores diferentes são confusos como o inferno;) #
0000 Voo Voo

21

Se você tiver dois Colorobjetos c1e c2, poderá comparar cada valor RGB com o valor c1de 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());

Esses valores podem ser divididos pela quantidade de saturações de diferença (255) e você obterá a diferença entre os dois.

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

Após o qual você pode apenas encontrar a diferença média de cores em porcentagem.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

O que lhe daria uma diferença percentual entre c1e c2.


Mais duas coisas menores: <b> 1 </b> pctDiffRed = diffRed / 255;lhe dará 0, a menos que você jogue para um flutuador em algum lugar. <b> 2 </b> Você precisará multiplicar por 100 em algum lugar para obter uma porcentagem.
precisa saber é o seguinte

18
Isso pode não dar a melhor diferença "visível", pois o olho humano percebe a cor mudar de maneira diferente. Dito isto, acho que é exatamente isso que ela está procurando, porque ela provavelmente está procurando uma diferença igualmente quantificável em vez de uma diferença percebida. Pensei que eu fosse aqui como algo a considerar, caso seja relevante.
Beska

14

Um dos melhores métodos para comparar duas cores pela percepção humana é o CIE76. A diferença é chamada Delta-E. Quando é menor que 1, o olho humano não consegue reconhecer a diferença.

Há uma maravilhosa classe de utilitários de cores ColorUtils (código abaixo), que inclui métodos de comparação CIE76. É escrito por Daniel Strebel, Universidade de Zurique.

No ColorUtils.class eu uso o método:

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

r1, g1, b1 - valores RGB da primeira cor

r2, g2, b2 - valores RGB da segunda cor que você deseja comparar

Se você trabalha com Android, pode obter estes valores assim:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class de Daniel Strebel, Universidade de Zurique:

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

o código acima apresenta um erro no rgb2lab: a divisão por 12 deve ser substituída pela divisão por 12,92 na conversão de r, g e b. caso contrário, a função não é contínua em R = 0,04045
John Smith

10

Apenas mais uma resposta, embora seja semelhante à do Supr - apenas um espaço de cores diferente.

A questão é: os seres humanos percebem a diferença de cores de maneira não uniforme e o espaço de cores RGB está ignorando isso. Como resultado, se você usar o espaço de cores RGB e apenas calcular a distância euclidiana entre duas cores, poderá obter uma diferença matematicamente absolutamente correta, mas que não coincidiria com o que os humanos lhe diriam.

Isso pode não ser um problema - acho que a diferença não é tão grande, mas se você quiser resolver isso "melhor", converta suas cores RGB em um espaço de cores projetado especificamente para evitar o problema acima. Existem vários aprimoramentos de modelos anteriores (já que isso é baseado na percepção humana, precisamos medir os valores "corretos" com base em dados experimentais). Existe o espaço de cores do laboratório que eu acho que seria o melhor, embora um pouco complicado para convertê-lo. Mais simples seria o CIE XYZ .

Aqui está um site que lista as fórmulas para converter entre diferentes espaços de cores, para que você possa experimentar um pouco.


3

Todos os métodos abaixo resultam em uma 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;
    }
}

2

A melhor maneira é deltaE. DeltaE é um número que mostra a diferença de cores. Se deltae <1, a diferença não pode ser reconhecida pelos olhos humanos. Eu escrevi um código em canvas e js para converter rgb em laboratório e depois calcular delta e. Neste exemplo, o código está reconhecendo pixels que possuem cores diferentes com uma cor base que eu salvei como LAB1. e se for diferente, torna esses pixels vermelhos. Você pode aumentar ou reduzir a sensibilidade da diferença de cores, aumentando ou diminuindo o intervalo aceitável de delta e. Neste exemplo, designei 10 para deltaE na linha que escrevi (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>

1
Estou um pouco preocupado com algumas de suas divisões inteiras. 1/3e 16/116ambos avaliam 0, o que quase certamente não é o que você deseja. Provavelmente seu algoritmo está correto, mas seu código certamente não está.
Dawood ibn Kareem

Você está descrevendo o CIE-LAB dE94. Delta E significa a mudança no Euclidiano. Ou seja, no espaço de cores padrão do laboratório, a distância euclidiana dada por sua fórmula de distância euclidiana muito padrão. Enquanto as modificações no Delta E, ou seja, 76, 94, 2000 (também existe o Delta E, CMC, que é usado para têxteis e similares), são diferentes fórmulas de distância entre as posições no espaço de cores do Lab. O código para o laboratório é o mesmo em cada um, o código para a diferença de cores não é. . Em suma, Delta E, não é o que se chama.
Tatarize 27/05

2

Um método simples que usa apenas 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)

Eu usei este por um tempo agora, e funciona bem o suficiente para a maioria dos propósitos.


Usando fórmula acima, qual é o intervalo de valores para a distância
Aman Aggarwal

isso é bem próximo da aproximação da diferença de cores euclidiana. Suponho que ele esteja pulando o componente raiz para acelerar o cálculo, por isso é um intervalo de 0 a 100 ^ 3. Se você deseja normalizar para 100, faça a distância ao poder de #1/3
1128 Daniel Daniel

2

Eu usei isso no meu android e parece satisfatório, embora o espaço RGB não seja recomendado:

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

Então usei o seguinte para obter porcentagem de similaridade:

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

Funciona bem o suficiente.


2

Eu tentei vários métodos como espaço de cores LAB, comparações HSV e descobri que a luminosidade funciona muito bem para esse fim.

Aqui está a versão do 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))

Darei à você

0.0687619047619048

Qual é a origem de ImageColor? edit Eu encontrei, é #from PIL import ImageColor
ademar111190 18/10/19

A luminosidade não é o brilho de uma cor? Portanto, neste caso, uma cor verde, azul e vermelha não seria diferente se o brilho fosse o mesmo?
Peter B.

1

Espero que você queira analisar uma imagem inteira no final, não é? Assim, você pode verificar a menor / maior diferença na matriz de cores da identidade.

A maioria das operações matemáticas para processamento de gráficos usa matrizes, porque os possíveis algoritmos que os utilizam são geralmente mais rápidos que os cálculos ponto a ponto clássicos de distância e comparação. (por exemplo, para operações usando DirectX, OpenGL, ...)

Então eu acho que você deveria começar por aqui:

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

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

... e como Beska já comentou acima:

Isso pode não dar a melhor diferença "visível" ...

O que significa também que seu algoritmo depende da definição de "similar a" se você estiver processando imagens.


1

Versão Kotlin com quanto por cento você deseja corresponder.

Chamada de método com o argumento opcional de porcentagem

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

Corpo do 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
}

0

Você precisará converter todas as cores RGB no espaço de cores do laboratório para poder compará-las da maneira que os humanos as veem. Caso contrário, você obterá cores RGB que 'combinam' de maneiras muito estranhas.

O link da Wikipedia sobre Diferenças de cores fornece uma introdução aos vários algoritmos de diferença de espaço de cores do laboratório que foram definidos ao longo dos anos. O mais simples que apenas verifica a distância euclidiana de duas cores de laboratório, funciona, mas tem algumas falhas.

Convenientemente, há uma implementação em Java do algoritmo CIEDE2000 mais sofisticado no projeto OpenIMAJ . Forneça seus dois conjuntos de cores de laboratório e retornará o valor da distância única.


0

A única maneira "correta" de comparar cores é fazê-lo com deltaE no CIELab ou CIELuv.

Mas, para muitas aplicações, acho que essa é uma aproximação suficientemente boa:

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

Eu acho que uma distância ponderada de Manhattan faz muito mais sentido ao comparar cores. Lembre-se de que as cores primárias estão apenas na nossa cabeça. Eles não têm nenhum significado físico. O CIELab e o CIELuv são modelados estatisticamente a partir da nossa percepção da cor.


0

Para rápido e sujo, você pode fazer

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

fazendo uso da divisão inteira para quantizar as cores.


0

Resposta rápida 5

Encontrei este tópico porque precisava de uma versão Swift desta pergunta. Como ninguém respondeu com a solução, aqui está a minha:

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)

Defino menos de 10% de diferença para retornar cores semelhantes, mas você pode personalizar isso sozinho.


0

API do Android for ColorUtils RGBToHSL: eu tinha duas cores int argb (cor1, cor2) e queria obter distância / diferença entre as duas cores. Aqui está o que eu fiz;

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

Então usei o código abaixo para encontrar a distância entre as duas cores.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.