Comment comparer deux couleurs pour la similitude / la différence


171

Je veux concevoir un programme qui puisse m'aider à évaluer entre 5 couleurs prédéfinies laquelle est la plus similaire à une couleur variable, et avec quel pourcentage. Le fait est que je ne sais pas comment faire cela manuellement étape par étape. Il est donc encore plus difficile de penser à un programme.

Plus de détails: Les couleurs sont issues de photographies de tubes avec gel qui ont des couleurs différentes. J'ai 5 tubes de couleurs différentes, chacun représentant 1 des 5 niveaux. Je veux prendre des photos d'autres échantillons et évaluer sur l'ordinateur à quel niveau appartient cet échantillon en comparant les couleurs, et je veux savoir cela avec un pourcentage d'approximation également. Je voudrais un programme qui fait quelque chose comme ceci: http://www.colortools.net/color_matcher.html

Si vous pouvez me dire quelles mesures prendre, même si ce sont des choses que je dois penser et faire manuellement. Ce serait très utile.


1
J'ai apporté une modification mineure au texte, en changeant un mot portugais en ce que je pense être l'équivalent anglais correct ... changez-le si je me suis trompé.
Beska

13
Il y a un article wikipedia sur la différence de couleur: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Cela devrait être intéressant: stevehanov.ca/blog/index.php?id=116 Il explore le calcul de la différence dans trois modèles de couleurs différents.
Vlad

Hey @OcasoProtal, c'est un excellent lien merci pour le partage. Et au PO, question intéressante.
Perception le

Essayez également de minimiser toute variablité photographique potentielle ... plus de détails dans la réponse ci-dessous.
Beska

Réponses:


130

Voir l'article de Wikipedia sur la différence de couleur pour les bonnes pistes. Fondamentalement, vous souhaitez calculer une métrique de distance dans un espace colorimétrique multidimensionnel. Mais RVB n'est pas "perceptuellement uniforme", donc votre mesure de distance RVB euclidienne suggérée par Vadim ne correspondra pas à la distance perçue par l'homme entre les couleurs. Pour commencer, L a b * est destiné à être un espace colorimétrique perceptuellement uniforme, et la métrique deltaE est couramment utilisée. Mais il existe des espaces de couleurs plus raffinés et des formules deltaE plus raffinées qui se rapprochent de la perception humaine.

Vous devrez en savoir plus sur les espaces couleurs et les illuminants pour effectuer les conversions. Mais pour une formule rapide qui est meilleure que la métrique RVB euclidienne, faites simplement ceci: supposez que vos valeurs RVB sont dans l'espace colorimétrique sRGB, trouvez les formules de conversion sRGB en L a b *, convertissez vos couleurs sRGB en L a b *, et calculez deltaE entre vos deux valeurs L a b *. Ce n'est pas cher en calcul, ce ne sont que des formules non linéaires et des multiplications et des ajouts.


11
+1 pour "deltaE", c'est la méthode de comparaison la plus standardisée et il existe des adaptations de la formule deltaE pour différents cas d'utilisation.
Martin Hennings

9
Vous pouvez trouver les formules de conversion ici: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Ou, si vous travaillez dans Ruby, consultez le colorgem qui implémente deltaE parmi d'autres opérations de couleur.
Mike Jarema

Voici l'essentiel de l'implémentation ci-dessus en Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Juste une idée qui m'est venue à l'esprit (désolé si stupide). Trois composants de couleurs peuvent être considérés comme des coordonnées 3D de points, puis vous pouvez calculer la distance entre les points.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

La distance entre les couleurs est

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

Le pourcentage est

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

28
Si nous utilisons l'espace de couleurs RVB, la différence entre 2 couleurs n'est pas la même que la façon dont les humains perçoivent la différence. Mais oui, l'idée de base est la même partout - nous aurions juste à la mapper dans un autre espace colorimétrique (laboratoire je pense)
Voo

6
@Voo: Je suis d'accord, HSV / HSL / LAB serait des espaces colorimétriques nettement meilleurs que le (s) RVB pour la correspondance de similarité basée sur la distance.
Jon Purdy

4
C'est un bon moyen de vous dire à quel point deux couleurs SONT différentes, mais cela ne vous dit pas à quel point elles seront différentes. Les yeux humains sont loin d'être parfaits: nous sommes plus sensibles au vert qu'au rouge ou au bleu, notre perception de la luminosité est logrithmique, etc. OP n'a jamais précisé ce qu'il voulait; mais voyez ici un algorithme spécialement conçu pour la vue humaine.
BlueRaja - Danny Pflughoeft

+ C'est aussi ma première idée.
ST3

9
Un autre problème ici est que 255, 0, 0 sont la même distance de 0, 255, 0 que 0, 0, 255.

27

en fait, j'ai emprunté le même chemin il y a quelques mois. il n'y a pas de réponse parfaite à la question (qui a été posée ici quelques fois) mais il y en a une plus sophistiquée que la réponse sqrt (rr) etc et plus facile à implémenter directement avec RVB sans passer à toutes sortes d'espaces colorimétriques alternatifs. J'ai trouvé cette formule ici qui est une approximation peu coûteuse de la formule réelle assez compliquée (par le CIE qui est le W3C de la couleur, puisque ce n'est pas une quête terminée, vous pouvez y trouver des équations de différence de couleur plus anciennes et plus simples). bonne chance

Edit: Pour la postérité, voici le code C pertinent:

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

cette méthode a fonctionné pour moi. Cela m'a aidé à trouver la couleur la plus proche de la liste des noms de couleur.
faisalbhagat

23

Une valeur de couleur a plus d'une dimension, il n'y a donc aucun moyen intrinsèque de comparer deux couleurs. Vous devez déterminer pour votre cas d'utilisation la signification des couleurs et ainsi la meilleure façon de les comparer.

Il est fort probable que vous souhaitiez comparer les propriétés de teinte, de saturation et / ou de luminosité des couleurs par opposition aux composants rouge / vert / bleu. Si vous avez du mal à comprendre comment vous voulez les comparer, prenez quelques paires d'échantillons de couleurs et comparez-les mentalement, puis essayez de vous justifier / vous expliquer pourquoi elles sont similaires / différentes.

Une fois que vous savez quelles propriétés / composants des couleurs vous souhaitez comparer, vous devez déterminer comment extraire ces informations d'une couleur.

Très probablement, vous aurez juste besoin de convertir la couleur de la représentation commune RedGreenBlue en HueSaturationLightness, puis calculer quelque chose comme

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

Cet exemple vous donnerait une valeur scalaire simple indiquant la distance entre le dégradé / la teinte des couleurs.

Voir HSL et HSV sur Wikipedia .


2
D'après les choses dont je me souviens de mes conférences sur ces choses, je convertirais l'image dans l'espace colorimétrique Lab et non HSV / HSL. Un raisonnement pour choisir celui-là?
Voo le

Nan. RVB et HSL sont ceux que je connais le mieux, j'ai donc choisi HSL juste pour souligner l'idée que le RVB "par défaut" n'est pas la seule option - cela dépend vraiment de l'application. Merci de m'avoir informé de l'espace colorimétrique Lab.
Supr

1
Je vous ai quand même donné +1 parce que le principe de base ici est la "bonne" réponse (convertir en espace colorimétrique qui gère uniformément la différence perçue puis faire la comparaison). Je ne sais pas trop quel espace serait le meilleur - tous ces différents espaces colorimétriques sont déroutants comme l'enfer imo;)
Voo

21

Si vous avez deux Colorobjets c1et c2, vous pouvez simplement comparer chaque valeur RVB de c1avec celle 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());

Ces valeurs, vous pouvez simplement diviser par la quantité de saturations de différence (255), et vous obtiendrez la différence entre les deux.

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

Après quoi, vous pouvez simplement trouver la différence de couleur moyenne en pourcentage.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Ce qui vous donnerait une différence de pourcentage entre c1et c2.


Deux autres choses mineures: <b> 1 </b> pctDiffRed = diffRed / 255;va vous donner 0 à moins que vous ne lanciez quelque part sur un flotteur. <b> 2 </b> Vous devrez multiplier par 100 quelque part pour obtenir un pourcentage.
vaughandroid

18
Cela peut ne pas donner la meilleure différence «visible», car l'œil humain perçoit les changements de couleur différemment. Cela étant dit, je suppose que c'est exactement ce qu'elle recherche, car elle recherche probablement une différence tout aussi quantifiable plutôt qu'une différence perçue. Je pensais juste que je ferais cela ici comme quelque chose à considérer au cas où cela serait pertinent.
Beska

14

L'une des meilleures méthodes pour comparer deux couleurs par perception humaine est la CIE76. La différence s'appelle Delta-E. Lorsqu'il est inférieur à 1, l'œil humain ne peut pas reconnaître la différence.

Il existe une merveilleuse classe d'utilitaires de couleur ColorUtils (code ci-dessous), qui comprend des méthodes de comparaison CIE76. Il est écrit par Daniel Strebel, Université de Zurich.

De ColorUtils.class, j'utilise la méthode:

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

r1, g1, b1 - valeurs RVB de la première couleur

r2, g2, b2 - valeurs RVB de la deuxième couleur que vous souhaitez comparer

Si vous travaillez avec Android, vous pouvez obtenir ces valeurs comme ceci:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class par Daniel Strebel, Université 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));
}
}

le code ci-dessus a une erreur dans rgb2lab: la division par 12 doit être remplacée par la division par 12,92 dans la conversion r, g et b. sinon la fonction n'est pas continue à r = 0,04045
John Smith

10

Juste une autre réponse, bien que similaire à celle de Supr - juste un espace colorimétrique différent.

Le fait est que les humains ne perçoivent pas la différence de couleur de manière uniforme et l'espace colorimétrique RVB l'ignore. En conséquence, si vous utilisez l'espace colorimétrique RVB et que vous calculez simplement la distance euclidienne entre 2 couleurs, vous pouvez obtenir une différence qui est mathématiquement absolument correcte, mais qui ne coïnciderait pas avec ce que les humains vous diraient.

Ce n'est peut-être pas un problème - la différence n'est pas si grande que je pense, mais si vous voulez résoudre ce «mieux», vous devez convertir vos couleurs RVB en un espace colorimétrique spécialement conçu pour éviter le problème ci-dessus. Il y en a plusieurs, des améliorations par rapport aux modèles précédents (puisque cela est basé sur la perception humaine, nous devons mesurer les valeurs "correctes" basées sur des données expérimentales). Il y a l' espace colorimétrique Lab qui, à mon avis, serait le meilleur, bien qu'un peu compliqué à convertir. Plus simple serait le CIE XYZ .

Voici un site qui répertorie les formules à convertir entre différents espaces colorimétriques afin que vous puissiez expérimenter un peu.


3

Toutes les méthodes ci-dessous donnent une échelle de 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;
    }
}

2

Le meilleur moyen est deltaE. DeltaE est un nombre qui montre la différence des couleurs. Si deltae <1, la différence ne peut pas être reconnue par les yeux humains. J'ai écrit un code en canvas et js pour convertir rgb en lab, puis calculer delta e. Sur cet exemple, le code reconnaît des pixels qui ont une couleur différente avec une couleur de base que j'ai enregistrée en tant que LAB1. et puis si c'est différent rend ces pixels rouges. Vous pouvez augmenter ou réduire la sensibilité de la différence de couleur par incréments ou diminuer la plage acceptable de delta e. Dans cet exemple, j'ai attribué 10 pour deltaE dans la ligne que j'ai écrite (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
Je suis un peu inquiet par certaines de vos divisions entières. 1/3et les 16/116deux évaluent 0, ce qui n'est certainement pas ce que vous voulez. Votre algorithme est probablement correct, mais votre code ne l'est certainement pas.
Dawood ibn Kareem

Vous décrivez le CIE-LAB dE94. Delta E signifie le changement euclidien. C'est-à-dire dans l'espace colorimétrique standard Lab, la distance euclidienne donnée par votre formule de distance euclidienne très standard. Alors que les modifications du Delta E, à savoir 76, 94, 2000 (il y a aussi Delta E, CMC qui est utilisé pour les textiles et autres) sont des formules de distance différentes entre les positions dans l'espace colorimétrique Lab. Le code du laboratoire est le même dans chacun d'eux, le code de la différence de couleur ne l'est pas. . Bref, Delta E, ce n'est pas ce qu'on appelle.
Tatarize

2

Une méthode simple qui n'utilise que RVB est

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)

J'utilise celui-ci depuis un moment maintenant, et il fonctionne assez bien pour la plupart des cas.


En utilisant la formule ci-dessus, quelle est la plage de valeurs pour la distance
Aman Aggarwal

c'est assez proche de l'approximation de la différence de couleur euclidienne. J'imagine qu'il saute le composant racine pour accélérer le calcul, donc c'est une plage de 0 à 100 ^ 3. Si vous voulez normaliser à 100, faites la distance à la puissance de1/3
Daniel

2

Je l'ai utilisé dans mon Android et cela semble satisfaisant bien que l'espace RVB ne soit pas recommandé:

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

Ensuite, j'ai utilisé ce qui suit pour obtenir un pourcentage de similitude:

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

Cela fonctionne assez bien.


2

J'ai essayé diverses méthodes comme l'espace colorimétrique LAB, les comparaisons HSV et j'ai trouvé que la luminosité fonctionne plutôt bien à cette fin.

Voici la version 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 donnera

0.0687619047619048

De quelle origine ImageColor? modifier j'ai trouvé, c'estfrom PIL import ImageColor
ademar111190

La luminosité n'est-elle pas la luminosité d'une couleur? Donc, dans ce cas, une couleur verte, bleue et rouge ne serait pas signalée comme étant différente tant que la luminosité est la même?
Peter B.

1

Je suppose que vous voulez analyser une image entière à la fin, n'est-ce pas? Vous pouvez donc vérifier la différence la plus petite / la plus élevée par rapport à la matrice de couleur d'identité.

La plupart des opérations mathématiques de traitement des graphiques utilisent des matrices, car les algorithmes possibles les utilisant sont souvent plus rapides que les calculs classiques de distance et de comparisme point par point. (par exemple pour les opérations utilisant DirectX, OpenGL, ...)

Je pense donc que vous devriez commencer ici:

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

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

... et comme Beska l'a déjà commenté ci-dessus:

Cela peut ne pas donner la meilleure différence "visible" ...

Ce qui signifie également que votre algorithme dépend de votre définition de "similaire à" si vous traitez des images.


1

Version Kotlin avec combien de pour cent voulez-vous correspondre.

Appel de méthode avec argument optionnel pour cent

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

Corps de la méthode

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

Vous devrez convertir toutes les couleurs RVB dans l'espace colorimétrique Lab pour pouvoir les comparer de la manière dont les humains les voient. Sinon, vous obtiendrez des couleurs RVB qui «correspondent» de manière très étrange.

Le lien wikipedia sur les différences de couleur vous donne une introduction aux différents algorithmes de différence d'espace colorimétrique Lab qui ont été définis au fil des ans. Le plus simple qui vérifie simplement la distance euclidienne de deux couleurs de laboratoire, fonctionne mais présente quelques défauts.

Il existe une implémentation Java de l'algorithme CIEDE2000 plus sophistiqué dans le projet OpenIMAJ . Fournissez-lui vos deux ensembles de couleurs Lab et cela vous donnera une valeur de distance unique.


0

La seule «bonne» façon de comparer les couleurs est de le faire avec deltaE dans CIELab ou CIELuv.

Mais pour beaucoup d'applications, je pense que c'est une assez bonne approximation:

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

Je pense qu'une distance de Manhattan pondérée a beaucoup plus de sens lorsque l'on compare les couleurs. N'oubliez pas que les couleurs primaires ne sont que dans notre tête. Ils n'ont aucune signification physique. CIELab et CIELuv sont modélisés statistiquement à partir de notre perception de la couleur.


0

Pour rapide et sale, vous pouvez faire

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

en utilisant la division entière pour quantifier les couleurs.


0

Réponse Swift 5

J'ai trouvé ce fil parce que j'avais besoin d'une version Swift de cette question. Comme personne n'a répondu avec la solution, voici la mienne:

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

Usage:

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

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

J'ai défini une différence de moins de 10% pour renvoyer des couleurs similaires, mais vous pouvez le personnaliser vous-même.


0

Android pour l'API ColorUtils RGBToHSL: j'avais deux couleurs argb int (color1, color2) et je voulais obtenir la distance / différence entre les deux couleurs. Voici ce que j'ai fait;

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

Ensuite, j'ai utilisé le code ci-dessous pour trouver la distance entre les deux couleurs.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.