Come confrontare due colori per somiglianza / differenza


171

Voglio progettare un programma che possa aiutarmi a valutare tra 5 colori predefiniti quale è più simile a un colore variabile e con quale percentuale. Il fatto è che non so come farlo manualmente passo dopo passo. Quindi è ancora più difficile pensare a un programma.

Maggiori dettagli: I colori provengono da fotografie di tubi con gel che come colori diversi. Ho 5 tubi con colori diversi dove ognuno è rappresentativo di 1 su 5 livelli. Voglio fare fotografie di altri campioni e al computer valutare a quale livello appartiene quel campione confrontando i colori, e voglio saperlo anche con una percentuale di approssimazione. Vorrei un programma che fa qualcosa del genere: http://www.colortools.net/color_matcher.html

Se puoi dirmi quali passi adottare, anche se sono cose che devo pensare e fare manualmente. Sarebbe molto utile


1
Ho apportato una piccola modifica al testo, cambiando una parola portoghese in quello che penso sia l'equivalente inglese corretto ... cambiarlo di nuovo se ho sbagliato.
Beska,

13
C'è un articolo di Wikipedia sulla differenza di colore: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Questo dovrebbe essere interessante: stevehanov.ca/blog/index.php?id=116 Esplora il calcolo della differenza in tre diversi modelli di colore.
Vlad

Ehi @OcasoProtal, è un ottimo collegamento grazie per la condivisione. E all'OP, domanda interessante.
Percezione

Cerca di ridurre al minimo qualsiasi potenziale variabilità fotografica ... Maggiori dettagli nella risposta di seguito.
Beska,

Risposte:


130

Vedi l'articolo di Wikipedia sulla differenza di colore per i cavi giusti. Fondamentalmente, si desidera calcolare una metrica della distanza in uno spazio colore multidimensionale. Ma RGB non è "uniforme percettivamente", quindi la tua metrica di distanza RGB euclidea suggerita da Vadim non corrisponderà alla distanza percepita dall'uomo tra i colori. Per cominciare, L a b * è inteso come uno spazio colore percettivamente uniforme e la metrica deltaE è comunemente usata. Ma ci sono spazi colore più raffinati e formule deltaE più raffinate che si avvicinano alla corrispondenza della percezione umana.

Dovrai saperne di più sugli spazi colore e sugli illuminanti per fare le conversioni. Ma per una formula rapida che è migliore della metrica RGB euclidea, basta fare questo: supponi che i tuoi valori RGB siano nello spazio colore sRGB, trova le formule di conversione sRGB in L a b *, converti i colori sRGB in L a b *, e calcola deltaE tra i tuoi due valori L a b *. Non è costoso dal punto di vista computazionale, è solo alcune formule non lineari e alcune moltiplicazioni e aggiunte.


11
+1 per "deltaE", questo è il metodo di confronto più standardizzato e ci sono adattamenti della formula deltaE per diversi casi d'uso.
Martin Hennings,

9
Puoi trovare le formule di conversione qui: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Oppure, se lavori in Ruby, controlla la colorgemma che implementa deltaE tra le altre operazioni di colore.
Mike Jarema,

Ecco una sintesi per l'implementazione di cui sopra in Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Solo un'idea che mi è venuta in mente per la prima volta (scusate se stupido). Si possono assumere tre componenti di colori coordinate 3D dei punti e quindi è possibile calcolare la distanza tra i punti.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

La distanza tra i colori è

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

La percentuale è

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

28
Se stiamo usando lo spazio colore RGB, la differenza tra 2 colori non è la stessa di come gli umani percepiscono la differenza. Ma sì, l'idea di base è la stessa ovunque - dovremmo solo mapparla in un altro spazio di colore (laboratorio credo)
Voo

6
@Voo: sono d'accordo, HSV / HSL / LAB sarebbe spazi di colore significativamente migliori di (s) RGB per la corrispondenza di somiglianza basata sulla distanza.
Jon Purdy,

4
Questo è un buon modo per dirti quanto sono diversi i due colori, ma fa un cattivo lavoro nel dirti quanto saranno PERCEPITI. Gli occhi umani sono tutt'altro che perfetti: siamo più sensibili al verde che al rosso o al blu, la nostra percezione della luminosità è logritmica, ecc. OP non ha mai specificato quale desidera; ma vedi qui per un algoritmo su misura per la vista umana.
BlueRaja - Danny Pflughoeft,

+ È anche la mia prima idea.
ST3,

9
Un altro problema qui è 255, 0, 0 è la stessa distanza da 0, 255, 0 in quanto è 0, 0, 255.

27

in realtà ho seguito lo stesso percorso un paio di mesi fa. non esiste una risposta perfetta alla domanda (che è stata posta qui un paio di volte) ma ce n'è una più sofisticata della risposta sqrt (rr) ecc. e più facile da impiantare direttamente con RGB senza passare a tutti i tipi di spazi colore alternativi. Ho trovato questa formula qui che è un'approssimazione a basso costo della formula reale piuttosto complicata (dal CIE che è il W3C di colore, poiché questa è una ricerca non finita, puoi trovare lì equazioni di differenza di colore più vecchie e più semplici). in bocca al lupo

Modifica: per i posteri, ecco il codice C pertinente:

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

questo metodo ha funzionato per me. Mi ha aiutato a trovare il colore più vicino dall'elenco dei nomi dei colori.
faisalbhagat,

23

Un valore di colore ha più di una dimensione, quindi non esiste un modo intrinseco per confrontare due colori. Devi determinare per il tuo caso d'uso il significato dei colori e quindi come confrontarli al meglio.

Molto probabilmente si desidera confrontare le proprietà di tonalità, saturazione e / o luminosità dei colori rispetto ai componenti rosso / verde / blu. Se hai problemi a capire come confrontarli, prendi alcune coppie di colori campione e confrontali mentalmente, quindi cerca di giustificare / spiegare a te stesso perché sono simili / diversi.

Una volta che sai quali proprietà / componenti dei colori vuoi confrontare, devi capire come estrarre tali informazioni da un colore.

Molto probabilmente dovrai solo convertire il colore dalla comune rappresentazione RedGreenBlue in HueSaturationLightness e quindi calcolare qualcosa di simile

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

Questo esempio ti darebbe un semplice valore scalare che indica quanto sono distanti la sfumatura / tonalità dei colori.

Vedi HSL e HSV su Wikipedia .


2
Dalle cose che ricordo dalle mie lezioni su queste cose, convertivo l'immagine nello spazio colore Lab e non in HSV / HSL. Qualche ragionamento per sceglierlo?
Voo,

No. RGB e HSL sono quelli con cui ho più familiarità, quindi ho scelto HSL solo per sottolineare l'idea che l'RGB "predefinito" non è l'unica opzione: dipende davvero dall'applicazione. Grazie per avermi fatto conoscere lo spazio colore Lab.
Supr

1
Ti ho dato +1 comunque perché il principio di base qui è la risposta "giusta" (converti nello spazio colore che gestisce uniformemente la differenza percepita, quindi fai un confronto). Non sono sicuro di quale spazio sarebbe il migliore - tutti questi diversi spazi colore sono confusi come l'inferno;)
Voo

21

Se hai due Coloroggetti c1e c2, puoi semplicemente confrontare ogni valore RGB c1con quello di 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());

Quei valori che puoi semplicemente dividere per la quantità di differenze di saturazione (255) e otterrai la differenza tra i due.

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

Dopo di che puoi semplicemente trovare la differenza di colore media in percentuale.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Che ti darebbe una differenza in percentuale tra c1e c2.


Altre 2 cose minori: <b> 1 </b> pctDiffRed = diffRed / 255;ti darà 0 a meno che tu non lanci un float da qualche parte. <b> 2 </b> Dovrai moltiplicare per 100 da qualche parte per ottenere una percentuale.
Vaughandroid,

18
Questo potrebbe non dare la migliore differenza "visibile", poiché l'occhio umano percepisce i cambiamenti di colore in modo diverso. Detto questo, immagino che sia esattamente quello che sta cercando, perché probabilmente sta cercando una differenza ugualmente quantificabile piuttosto che una differenza percepita. Ho solo pensato che questo qui come qualcosa da considerare nel caso fosse rilevante.
Beska,

14

Uno dei metodi migliori per confrontare due colori secondo la percezione umana è CIE76. La differenza si chiama Delta-E. Quando è inferiore a 1, l'occhio umano non può riconoscere la differenza.

Esiste una meravigliosa classe di utilità di colore ColorUtils (codice sotto), che include i metodi di confronto CIE76. È scritto da Daniel Strebel, Università di Zurigo.

Da ColorUtils.class utilizzo il metodo:

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

r1, g1, b1 - Valori RGB del primo colore

r2, g2, b2 - Valori RGB del secondo colore che si desidera confrontare

Se lavori con Android, puoi ottenere questi valori in questo modo:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils.class di Daniel Strebel, Università di Zurigo:

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

il codice sopra ha un errore in rgb2lab: la divisione per 12 deve essere sostituita dalla divisione per 12,92 nella conversione r, geb. altrimenti la funzione non è continua a r = 0,04045
John Smith,

10

Solo un'altra risposta, sebbene sia simile a quella di Supr - solo uno spazio colore diverso.

Il fatto è: gli umani percepiscono la differenza di colore in modo non uniforme e lo spazio cromatico RGB lo ignora. Di conseguenza, se si utilizza lo spazio colore RGB e si calcola semplicemente la distanza euclidea tra 2 colori, si potrebbe ottenere una differenza che è matematicamente assolutamente corretta, ma non coinciderebbe con ciò che gli umani ti direbbero.

Questo potrebbe non essere un problema - penso che la differenza non sia così grande, ma se vuoi risolvere questo "meglio" dovresti convertire i tuoi colori RGB in uno spazio cromatico progettato appositamente per evitare il problema sopra. Ce ne sono molti, miglioramenti rispetto ai modelli precedenti (poiché questo si basa sulla percezione umana, dobbiamo misurare i valori "corretti" sulla base di dati sperimentali). C'è lo spazio colore Lab che penso sia il migliore anche se un po 'complicato per convertirlo. Più semplice sarebbe quello del CIE XYZ .

Ecco un sito che elenca le formule da convertire tra diversi spazi colore in modo da poter sperimentare un po '.


3

Tutti i metodi seguenti danno come risultato una scala da 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

Il modo migliore è deltaE. DeltaE è un numero che mostra la differenza dei colori. Se delta <1, la differenza non può essere riconosciuta dagli occhi umani. Ho scritto un codice in canvas e js per convertire rgb in lab e poi calcolare delta e. In questo esempio il codice riconosce i pixel che hanno un colore diverso con un colore di base che ho salvato come LAB1. e poi se è diverso rende rossi quei pixel. È possibile aumentare o ridurre la sensibilità della differenza di colore aumentando o diminuendo l'intervallo accettabile di delta e. In questo esempio ho assegnato 10 per deltaE nella riga che ho scritto (delta <= 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
Sono un po 'preoccupato da alcune delle tue divisioni intere. 1/3ed 16/116entrambi valutano 0, che quasi sicuramente non è quello che vuoi. Probabilmente il tuo algoritmo è corretto, ma il tuo codice certamente no.
Dawood ibn Kareem,

Stai descrivendo CIE-LAB dE94. Delta E significa il cambiamento in Euclide. Vale a dire nello spazio colore standard Lab, la distanza euclidea data dalla tua formula di distanza euclidea molto standard. Considerando che le modifiche del Delta E, vale a dire 76, 94, 2000 (c'è anche Delta E, CMC che viene utilizzato per tessuti e simili) sono diverse formule di distanza tra le posizioni all'interno dello spazio colore Lab. Il codice per Lab è lo stesso in ciascuno, il codice per la differenza di colore non lo è. . In breve, Delta E, non è ciò che viene chiamato.
Tatarize

2

Un metodo semplice che utilizza solo 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)

L'ho usato per un po 'di tempo e funziona abbastanza bene per la maggior parte degli scopi.


Usando la formula sopra, qual è l'intervallo di valori per la distanza
Aman Aggarwal,

questo è abbastanza vicino all'approssimazione della differenza cromatica euclidea. Sto indovinando che sta saltando il componente radice per accelerare il calcolo, quindi è un intervallo da 0 a 100 ^ 3. Se vuoi normalizzare a 100, mantieni la distanza dalla potenza di1/3
Daniel

2

L'ho usato nel mio Android e sembra soddisfacente anche se lo spazio RGB non è raccomandato:

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

Quindi ho usato quanto segue per ottenere la percentuale di somiglianza:

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

Funziona abbastanza bene.


2

Ho provato vari metodi come lo spazio colore LAB, i confronti HSV e ho scoperto che la luminosità funziona abbastanza bene per questo scopo.

Ecco la versione di 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))

Ti darà

0.0687619047619048

Qual è l'origine di ImageColor? modifica Ho trovato, lo èfrom PIL import ImageColor
ademar111190

La luminosità non è la luminosità di un colore? Quindi in questo caso un colore verde, blu e rosso non verrebbe segnalato come diverso se la luminosità è la stessa?
Peter B.

1

Mi aspetto che tu voglia analizzare un'intera immagine alla fine, vero? In questo modo è possibile verificare la differenza minima / massima rispetto alla matrice dei colori dell'identità.

La maggior parte delle operazioni matematiche per l'elaborazione della grafica utilizzano matrici, poiché i possibili algoritmi che le utilizzano sono spesso più veloci dei classici calcoli punto per punto e confronto. (ad es. per operazioni che utilizzano DirectX, OpenGL, ...)

Quindi penso che dovresti iniziare qui:

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

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

... e come Beska ha già commentato sopra:

Questo potrebbe non dare la migliore differenza "visibile" ...

Ciò significa anche che il tuo algoritmo dipende dalla tua definizione di "simile a" se stai elaborando immagini.


1

Versione di Kotlin con la percentuale che vuoi abbinare.

Chiamata di metodo con argomento opzionale percentuale

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

Corpo del metodo

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

Dovrai convertire tutti i colori RGB nello spazio colore Lab per poterli confrontare nel modo in cui gli umani li vedono. Altrimenti otterrai colori RGB che 'corrispondono' in alcuni modi molto strani.

Il link di Wikipedia su Differenze di colore offre un'introduzione ai vari algoritmi di differenza di spazio cromatico Lab che sono stati definiti nel corso degli anni. Il più semplice che controlla solo la distanza euclidea di due colori di laboratorio, funziona ma presenta alcuni difetti.

Convenientemente esiste un'implementazione Java del più sofisticato algoritmo CIEDE2000 nel progetto OpenIMAJ . Fornisci i tuoi due set di colori Lab e ti restituirà il valore della distanza singola.


0

L'unico modo "giusto" per confrontare i colori è farlo con deltaE in CIELab o CIELuv.

Ma per molte applicazioni penso che questa sia un'approssimazione abbastanza buona:

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

Penso che una distanza ponderata di Manhattan abbia molto più senso quando si confrontano i colori. Ricorda che le primarie di colore sono solo nella nostra testa. Non hanno alcun significato fisico. CIELab e CIELuv sono modellati statisticamente dalla nostra percezione del colore.


0

Per veloce e sporco, puoi farlo

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

facendo uso della divisione intera per quantizzare i colori.


0

Risposta rapida 5

Ho trovato questa discussione perché avevo bisogno di una versione Swift di questa domanda. Poiché nessuno ha risposto con la soluzione, ecco la mia:

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)

Ho impostato una differenza inferiore al 10% per restituire colori simili, ma puoi personalizzarlo da solo.


0

Android per ColorUtils API RGBToHSL: avevo due colori int argb (color1, color2) e volevo ottenere la distanza / differenza tra i due colori. Ecco cosa ho fatto;

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

Quindi ho usato sotto il codice per trovare la distanza tra i due colori.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.