So vergleichen Sie zwei Farben auf Ähnlichkeit / Unterschied


171

Ich möchte ein Programm entwerfen, mit dessen Hilfe ich zwischen 5 vordefinierten Farben beurteilen kann, welche einer variablen Farbe ähnlicher sind und mit welchem ​​Prozentsatz. Die Sache ist, dass ich nicht weiß, wie man das manuell Schritt für Schritt macht. Es ist also noch schwieriger, sich ein Programm vorzustellen.

Weitere Details: Die Farben stammen von Fotografien von Röhrchen mit Gel, die unterschiedliche Farben haben. Ich habe 5 Röhren mit verschiedenen Farben, von denen jede für 1 von 5 Ebenen repräsentativ ist. Ich möchte Fotos von anderen Proben machen und am Computer beurteilen, zu welcher Ebene diese Probe gehört, indem ich Farben vergleiche, und ich möchte das auch mit einem Prozentsatz der Annäherung wissen. Ich möchte ein Programm, das ungefähr so ​​funktioniert: http://www.colortools.net/color_matcher.html

Wenn Sie mir sagen können, welche Schritte ich unternehmen soll, auch wenn ich sie manuell denken und tun muss. Es wäre sehr hilfreich.


1
Ich habe den Text geringfügig geändert und ein portugiesisches Wort in das meiner Meinung nach korrekte englische Äquivalent geändert. Ändern Sie es zurück, wenn ich mich geirrt habe.
Beska

13
Es gibt einen Wikipedia-Artikel über Farbunterschiede: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Dies sollte interessant sein: stevehanov.ca/blog/index.php?id=116 Es wird untersucht, wie der Unterschied in drei verschiedenen Farbmodellen berechnet wird.
Vlad

Hey @OcasoProtal, das ist ein großartiger Link, danke fürs Teilen. Und zum OP interessante Frage.
Wahrnehmung

Versuchen Sie auch, mögliche fotografische Schwankungen zu minimieren ... mehr Details in der Antwort unten.
Beska

Antworten:


130

Die richtigen Hinweise finden Sie im Wikipedia-Artikel zum Farbunterschied . Grundsätzlich möchten Sie eine Abstandsmetrik in einem mehrdimensionalen Farbraum berechnen. RGB ist jedoch nicht "wahrnehmungsmäßig einheitlich", sodass Ihre von Vadim vorgeschlagene euklidische RGB-Entfernungsmetrik nicht mit der vom Menschen wahrgenommenen Entfernung zwischen Farben übereinstimmt. Zunächst soll L a b * ein wahrnehmungsmäßig einheitlicher Farbraum sein, und die DeltaE-Metrik wird üblicherweise verwendet. Es gibt jedoch verfeinerte Farbräume und verfeinerte DeltaE-Formeln, die der menschlichen Wahrnehmung näher kommen.

Sie müssen mehr über Farbräume und Leuchtmittel erfahren, um die Konvertierungen durchführen zu können. Für eine schnelle Formel, die besser ist als die euklidische RGB-Metrik, gehen Sie einfach folgendermaßen vor: Nehmen Sie an, dass sich Ihre RGB-Werte im sRGB-Farbraum befinden, suchen Sie die Konvertierungsformeln von sRGB in L a b * und konvertieren Sie Ihre sRGB-Farben in L a b *. und berechne deltaE zwischen deinen beiden L a b * -Werten. Es ist nicht rechenintensiv, es sind nur einige nichtlineare Formeln und einige Multiplikationen und Additionen.


11
+1 für "deltaE", das ist die standardisierteste Vergleichsmethode, und es gibt Anpassungen der deltaE-Formel für verschiedene Anwendungsfälle.
Martin Hennings

9
Sie finden die Konvertierungsformeln hier: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Wenn Sie in Ruby arbeiten, sehen Sie sich den colorEdelstein an, der unter anderem DeltaE implementiert .
Mike Jarema

Hier ist eine Übersicht über die obige Implementierung in Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Nur eine Idee, die mir zuerst in den Sinn kam (sorry, wenn dumm). Drei Komponenten von Farben können als 3D-Koordinaten von Punkten angenommen werden, und dann können Sie den Abstand zwischen Punkten berechnen.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

Der Abstand zwischen den Farben beträgt

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

Prozentsatz ist

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

28
Wenn wir den RGB-Farbraum verwenden, ist der Unterschied zwischen zwei Farben nicht derselbe, wie Menschen den Unterschied wahrnehmen . Aber ja, die Grundidee ist überall dieselbe - wir müssten sie nur in einen anderen Farbraum abbilden (Labor würde ich denken)
Voo

6
@Voo: Ich stimme zu, HSV / HSL / LAB wären signifikant bessere Farbräume als (s) RGB für die entfernungsbasierte Ähnlichkeitsanpassung.
Jon Purdy

4
Dies ist eine gute Möglichkeit, Ihnen zu sagen, wie unterschiedlich zwei Farben sind, aber es ist eine schlechte Möglichkeit, Ihnen zu sagen, wie unterschiedlich sie wahrgenommen werden. Menschliche Augen sind alles andere als perfekt: Wir reagieren empfindlicher auf Grün als auf Rot oder Blau, unsere Helligkeitswahrnehmung ist logrithmisch usw. OP hat nie angegeben, welche er / sie möchte; aber sehen hier für einen Algorithmus speziell zugeschnitten für das menschliche Auge.
BlueRaja - Danny Pflughoeft

+ Es ist auch meine erste Idee.
ST3

9
Ein weiteres Problem hier ist 255, 0, 0 ist der gleiche Abstand von 0, 255, 0 wie 0, 0, 255.

27

Eigentlich bin ich vor ein paar Monaten den gleichen Weg gegangen. Es gibt keine perfekte Antwort auf die Frage (die hier einige Male gestellt wurde), aber es gibt eine differenziertere Antwort als die Antwort sqrt (rr) usw., die einfacher direkt mit RGB zu implementieren ist, ohne zu allen Arten von alternativen Farbräumen zu wechseln. Ich habe hier diese Formel gefunden, die eine kostengünstige Annäherung an die ziemlich komplizierte reale Formel darstellt (von der CIE, dem W3C der Farbe, da dies eine noch nicht abgeschlossene Aufgabe ist, finden Sie dort ältere und einfachere Farbdifferenzgleichungen). Viel Glück

Bearbeiten: Für die Nachwelt ist hier der relevante C-Code:

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

Diese Methode hat bei mir funktioniert. Es hat mir geholfen, die nächstgelegene Farbe aus der Liste der Farbnamen zu finden.
Faisalbhagat

23

Ein Farbwert hat mehr als eine Dimension, daher gibt es keine Möglichkeit, zwei Farben zu vergleichen. Sie müssen für Ihren Anwendungsfall die Bedeutung der Farben bestimmen und damit diese am besten vergleichen.

Höchstwahrscheinlich möchten Sie die Farbton-, Sättigungs- und / oder Helligkeitseigenschaften der Farben im Gegensatz zu den Rot / Grün / Blau-Komponenten vergleichen. Wenn Sie Probleme haben, herauszufinden, wie Sie sie vergleichen möchten, nehmen Sie einige Paar Musterfarben und vergleichen Sie sie mental. Versuchen Sie dann, sich selbst zu rechtfertigen / zu erklären, warum sie ähnlich / unterschiedlich sind.

Wenn Sie wissen, welche Eigenschaften / Komponenten der Farben Sie vergleichen möchten, müssen Sie herausfinden, wie Sie diese Informationen aus einer Farbe extrahieren können.

Höchstwahrscheinlich müssen Sie nur die Farbe aus der allgemeinen RedGreenBlue-Darstellung in HueSaturationLightness konvertieren und dann so etwas berechnen

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

In diesem Beispiel erhalten Sie einen einfachen Skalarwert, der angibt, wie weit der Farbverlauf / Farbton der Farben voneinander entfernt ist.

Siehe HSL und HSV bei Wikipedia .


2
Nach dem, woran ich mich aus meinen Vorlesungen über diese Dinge erinnere, würde ich das Bild in den Lab-Farbraum konvertieren und nicht in HSV / HSL. Irgendwelche Gründe für die Auswahl?
Voo

Nee. RGB und HSL sind die, mit denen ich am besten vertraut bin. Deshalb habe ich HSL ausgewählt, um die Idee zu unterstreichen, dass das "Standard" -RGB nicht die einzige Option ist - es hängt wirklich von der Anwendung ab. Vielen Dank, dass Sie mich über den Lab-Farbraum informiert haben.
Supr

1
Ich habe dir sowieso +1 gegeben, weil das Grundprinzip hier die "richtige" Antwort ist (in Farbraum konvertieren, der den wahrgenommenen Unterschied einheitlich behandelt, dann vergleiche). Ich bin mir nicht sicher, welcher Raum der beste wäre - all diese verschiedenen Farbräume sind verdammt verwirrend imo;)
Voo

21

Wenn Sie zwei ColorObjekte c1und haben c2, können Sie einfach jeden RGB-Wert c1mit dem von vergleichen 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());

Diese Werte können Sie einfach durch die Differenzsättigung (255) dividieren, und Sie erhalten die Differenz zwischen den beiden.

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

Danach können Sie nur noch den durchschnittlichen Farbunterschied in Prozent ermitteln.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Welches würde Ihnen einen prozentualen Unterschied zwischen c1und geben c2.


2 weitere Kleinigkeiten: <b> 1 </ b> pctDiffRed = diffRed / 255;gibt Ihnen 0, es sei denn, Sie werfen irgendwo auf einen Schwimmer. <b> 2 </ b> Sie müssen irgendwo mit 100 multiplizieren, um einen Prozentsatz zu erhalten.
Vaughandroid

18
Dies ergibt möglicherweise nicht den besten "sichtbaren" Unterschied, da das menschliche Auge Farbänderungen unterschiedlich wahrnimmt. Davon abgesehen schätze ich, dass dies genau das ist, wonach sie sucht, weil sie wahrscheinlich eher nach einem ebenso quantifizierbaren Unterschied als nach einem wahrgenommenen Unterschied sucht. Ich dachte nur, ich würde das hier draußen als etwas in Betracht ziehen, falls es relevant ist.
Beska

14

Eine der besten Methoden, um zwei Farben durch menschliche Wahrnehmung zu vergleichen, ist CIE76. Der Unterschied heißt Delta-E. Wenn es kleiner als 1 ist, kann das menschliche Auge den Unterschied nicht erkennen.

Es gibt eine wunderbare Farbdienstprogrammklasse ColorUtils (Code unten), die CIE76-Vergleichsmethoden enthält. Es wurde von Daniel Strebel, Universität Zürich, geschrieben.

Von ColorUtils.class verwende ich die Methode:

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

r1, g1, b1 - RGB-Werte der ersten Farbe

r2, g2, b2 - RGB-Werte der zweiten Farbe, die Sie vergleichen möchten

Wenn Sie mit Android arbeiten, können Sie folgende Werte erhalten:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class von Daniel Strebel, Universität Zürich:

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

Der obige Code hat einen Fehler in rgb2lab: Division durch 12 sollte durch Division durch 12,92 in der r-, g- und b-Konvertierung ersetzt werden. Andernfalls ist die Funktion bei r = 0,04045
John Smith am

10

Nur eine weitere Antwort, obwohl sie der von Supr ähnlich ist - nur ein anderer Farbraum.

Die Sache ist: Menschen nehmen den Farbunterschied nicht gleichmäßig wahr und der RGB-Farbraum ignoriert dies. Wenn Sie den RGB-Farbraum verwenden und nur den euklidischen Abstand zwischen zwei Farben berechnen, erhalten Sie möglicherweise einen Unterschied, der mathematisch absolut korrekt ist, aber nicht mit dem übereinstimmt, was Menschen Ihnen sagen würden.

Dies ist möglicherweise kein Problem - der Unterschied ist meiner Meinung nach nicht so groß, aber wenn Sie dieses "bessere" Problem lösen möchten, sollten Sie Ihre RGB-Farben in einen Farbraum konvertieren, der speziell entwickelt wurde, um das oben genannte Problem zu vermeiden. Es gibt mehrere Verbesserungen gegenüber früheren Modellen (da dies auf der menschlichen Wahrnehmung basiert, müssen wir die "richtigen" Werte basierend auf experimentellen Daten messen). Es gibt den Lab-Farbraum, von dem ich denke, dass er der beste ist, obwohl er etwas kompliziert zu konvertieren ist. Einfacher wäre das CIE XYZ .

Hier ist eine Seite, auf der die Formeln aufgelistet sind, die zwischen verschiedenen Farbräumen konvertiert werden sollen, damit Sie ein wenig experimentieren können.


3

Alle folgenden Methoden führen zu einer Skala von 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

Der beste Weg ist DeltaE. DeltaE ist eine Zahl, die den Unterschied der Farben anzeigt. Wenn Deltae <1 ist, kann der Unterschied mit menschlichen Augen nicht erkannt werden. Ich habe einen Code in canvas und js geschrieben, um rgb in lab zu konvertieren und dann Delta e zu berechnen. In diesem Beispiel erkennt der Code Pixel mit unterschiedlicher Farbe und einer Grundfarbe, die ich als LAB1 gespeichert habe. und wenn es dann anders ist, werden diese Pixel rot. Sie können die Empfindlichkeit des Farbunterschieds mit zunehmendem Wert erhöhen oder verringern oder den zulässigen Bereich von Delta e verringern. In diesem Beispiel habe ich in der von mir geschriebenen Zeile 10 für deltaE zugewiesen (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
Ich bin ein bisschen besorgt über einige Ihrer ganzzahligen Abteilungen. 1/3und 16/116beide bewerten 0, was mit ziemlicher Sicherheit nicht das ist, was Sie wollen. Wahrscheinlich ist Ihr Algorithmus korrekt, Ihr Code jedoch sicherlich nicht.
Dawood ibn Kareem

Sie beschreiben CIE-LAB dE94. Delta E bedeutet die Änderung des Euklidischen. Das heißt, im Standard-Lab-Farbraum der euklidische Abstand, der durch Ihre Standardformel für den euklidischen Abstand angegeben wird. Während Modifikationen des Delta E, nämlich 76, 94, 2000 (es gibt auch Delta E, CMC, das für Textilien und dergleichen verwendet wird) unterschiedliche Abstandsformeln zwischen Positionen innerhalb des Lab-Farbraums sind. Der Code für das Labor ist in jedem Fall derselbe, der Code für den Farbunterschied nicht. . Kurz gesagt, Delta E heißt nicht so.
Tatarize

2

Eine einfache Methode, die nur RGB verwendet, ist

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)

Ich habe dieses Produkt schon eine Weile verwendet und es funktioniert für die meisten Zwecke gut genug.


Was ist der Wertebereich für die Entfernung unter Verwendung der obigen Formel
Aman Aggarwal

Dies kommt der euklidischen Farbdifferenznäherung ziemlich nahe. Ich vermute, dass die Root-Komponente übersprungen wird, um die Berechnung zu beschleunigen. Es liegt also zwischen 0 und 100 ^ 3. Wenn Sie auf 100 normalisieren möchten, machen Sie Abstand zur Macht von1/3
Daniel

2

Ich habe dies in meinem Android verwendet und es scheint zufriedenstellend, obwohl RGB-Speicherplatz nicht empfohlen wird:

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

Dann habe ich Folgendes verwendet, um Prozent der Ähnlichkeit zu erhalten:

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

Es funktioniert gut genug.


2

Ich habe verschiedene Methoden wie den LAB-Farbraum und HSV-Vergleiche ausprobiert und festgestellt, dass die Leuchtkraft für diesen Zweck ziemlich gut funktioniert.

Hier ist die Python-Version

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

Werde dir geben

0.0687619047619048

Woher kommt das ImageColor? bearbeiten fand ich, es istfrom PIL import ImageColor
ademar111190

Ist Leuchtkraft nicht die Helligkeit einer Farbe? In diesem Fall würde eine grüne, blaue und rote Farbe nicht als unterschiedlich gemeldet, solange die Helligkeit gleich ist.
Peter B.

1

Ich gehe davon aus, dass Sie am Ende ein ganzes Bild analysieren möchten, nicht wahr? So können Sie nach dem kleinsten / höchsten Unterschied zur Identitätsfarbmatrix suchen.

Die meisten mathematischen Operationen zur Verarbeitung von Grafiken verwenden Matrizen, da die möglichen Algorithmen, die sie verwenden, häufig schneller sind als klassische Punkt-für-Punkt-Entfernungs- und Vergleichsberechnungen. (zB für Operationen mit DirectX, OpenGL, ...)

Also ich denke du solltest hier anfangen:

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

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

... und wie Beska bereits oben kommentiert hat:

Dies ergibt möglicherweise nicht den besten "sichtbaren" Unterschied ...

Dies bedeutet auch, dass Ihr Algorithmus von Ihrer Definition von "ähnlich wie" abhängt, wenn Sie Bilder verarbeiten.


1

Kotlin-Version mit wie viel Prozent möchten Sie übereinstimmen.

Methodenaufruf mit prozentualem optionalem Argument

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

Methodenkörper

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

Sie müssen alle RGB-Farben in den Lab-Farbraum konvertieren, um sie so vergleichen zu können, wie Menschen sie sehen. Andernfalls erhalten Sie RGB-Farben, die auf sehr seltsame Weise übereinstimmen.

Der Wikipedia-Link zu Farbunterschieden gibt Ihnen ein Intro in die verschiedene Lab - Farbraum Differenz Algorithmen , die im Laufe der Jahre festgelegt wurde. Das einfachste, das nur den euklidischen Abstand zweier Laborfarben überprüft, funktioniert, weist jedoch einige Mängel auf.

Praktischerweise gibt es eine Java-Implementierung des komplexeren CIEDE2000-Algorithmus im OpenIMAJ- Projekt. Geben Sie Ihre zwei Sätze von Laborfarben an, und Sie erhalten einen Wert für den einzelnen Abstand zurück.


0

Der einzige "richtige" Weg, Farben zu vergleichen, besteht darin, dies mit deltaE in CIELab oder CIELuv zu tun.

Aber für viele Anwendungen halte ich dies für eine hinreichend gute Annäherung:

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

Ich denke, eine gewichtete Manhattan-Distanz ist beim Vergleich von Farben viel sinnvoller. Denken Sie daran, dass Farbprimäre nur in unserem Kopf sind. Sie haben keine physikalische Bedeutung. CIELab und CIELuv werden statistisch aus unserer Farbwahrnehmung modelliert.


0

Für schnell und schmutzig können Sie tun

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

Verwendung der Ganzzahldivision zur Quantisierung der Farben.


0

Swift 5 Antwort

Ich habe diesen Thread gefunden, weil ich eine Swift-Version dieser Frage brauchte. Da niemand mit der Lösung geantwortet hat, ist hier meine:

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

Verwendung:

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

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

Ich habe einen Unterschied von weniger als 10% festgelegt, um ähnliche Farben zurückzugeben, aber Sie können dies selbst anpassen.


0

Android für ColorUtils API RGBToHSL: Ich hatte zwei int argb- Farben (color1, color2) und wollte Abstand / Unterschied zwischen den beiden Farben ermitteln. Hier ist was ich getan habe;

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

Dann habe ich den folgenden Code verwendet, um den Abstand zwischen den beiden Farben zu ermitteln.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.