Benzerlik / fark için iki renk nasıl karşılaştırılır


171

Değişken bir renge daha çok benzeyen önceden tanımlanmış 5 renk arasında ve yüzde olarak değerlendirmeme yardımcı olabilecek bir program tasarlamak istiyorum. Mesele şu ki, bunu manuel olarak adım adım nasıl yapacağımı bilmiyorum. Bu yüzden bir programı düşünmek daha da zor.

Daha fazla detay: Renkler, farklı renklerde jel içeren tüplerin fotoğraflarından. Her biri 5 seviyenin 1'ini temsil eden farklı renklere sahip 5 tüpüm var. Diğer örneklerin fotoğraflarını çekmek istiyorum ve bilgisayarda renkleri karşılaştırarak bu numunenin hangi seviyeye ait olduğunu değerlendirmek istiyorum ve bunu da yaklaşık yüzdeyle bilmek istiyorum. Böyle bir şey yapan bir program istiyorum: http://www.colortools.net/color_matcher.html

Bana hangi adımları atmam gerektiğini söyleyebiliyorsanız, onlar benim düşünmem ve elle yapmam gereken şeyler olsa bile. Çok yardımcı olur.


1
Metinde küçük bir değişiklik yaptım, Portekizce bir kelimeyi doğru İngilizce karşılığı olduğunu düşündüğüm şekilde değiştirdim ...
Beska

13
Renk farkı hakkında bir wikipedia makalesi var: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Bu ilginç olmalı: stevehanov.ca/blog/index.php?id=116 Üç farklı renk modelindeki farkı hesaplamayı araştırıyor.
Vlad

Hey @OcasoProtal, bu paylaşım için teşekkürler harika bir bağlantı. Ve OP'ye ilginç bir soru.
Algı

Herhangi bir potansiyel fotoğraf değişkenliğini en aza indirmeye çalışın ... aşağıda daha fazla ayrıntı.
Beska

Yanıtlar:


130

Doğru potansiyel müşteriler için Wikipedia'nın Renk Farkı hakkındaki makalesine bakın . Temel olarak, bazı çok boyutlu renk uzaylarında bir mesafe metriği hesaplamak istersiniz. Ancak RGB "algısal olarak tekdüze" değildir, bu nedenle Vadim tarafından önerilen Öklid RGB mesafe metriğiniz, renkler arasındaki insan tarafından algılanan mesafeyle eşleşmez. Başlangıç için La b *, algısal olarak muntazam bir renk uzayı olarak düşünülür ve deltaE metriği yaygın olarak kullanılır. Ancak daha uyumlu renk uzayları ve eşleşen insan algısına yaklaşan daha rafine deltaE formülleri vardır.

Dönüşümleri yapmak için renk uzayları ve aydınlatıcılar hakkında daha fazla bilgi edinmeniz gerekecek. Ancak Öklid RGB metriğinden daha iyi olan hızlı bir formül için şunu yapın: RGB değerlerinizin sRGB renk alanında olduğunu varsayalım, sRGB'yi L a b * dönüşüm formüllerine dönüştürün, sRGB renklerinizi L a b * 'ya dönüştürün, ve deltaE değerini iki L a b * değeriniz arasında hesaplayın . Hesaplama açısından pahalı değil, sadece bazı doğrusal olmayan formüller ve bazıları çoğalır ve ekler.


11
"DeltaE" için +1, bu en standart karşılaştırma yöntemidir ve farklı kullanım durumları için deltaE formülünün uyarlamaları vardır.
Martin Hennings

9
Dönüşüm formüllerini burada bulabilirsiniz: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Veya Ruby'de çalışıyorsanız, diğer renk işlemleri arasında deltaE uygulayan colormücevherlere bakın .
Mike Jarema


47

Aklıma ilk gelen bir fikir (aptal olduğum için üzgünüm). Renklerin üç bileşeni noktaların 3B koordinatları olarak kabul edilebilir ve daha sonra noktalar arasındaki mesafeyi hesaplayabilirsiniz.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

Renkler arasındaki mesafe

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

Yüzde

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

28
RGB renk aralığını kullanırsak, 2 renk arasındaki fark, insanların farkı nasıl algıladığı ile aynı değildir . Ama evet temel fikir her yerde aynı - sadece başka bir renk uzayıyla eşleştirmek zorundayız (sanırım laboratuvar)
Voo

6
@Voo: Katılıyorum, HSV / HSL / LAB mesafe tabanlı benzerlik eşleşmesi için RGB'den (s) RGB'den daha iyi renk uzayları olacaktır.
Jon Purdy

4
Bu, size iki farklı rengin ne kadar farklı olduğunu söylemenin iyi bir yoludur, ancak ne kadar farklı algılanacaklarını söylemenin kötü bir işidir. İnsan gözleri mükemmel olmaktan uzaktır: yeşile kırmızı veya maviden daha duyarlıyız, parlaklık algımız logrithmic vb. OP hangisini istediğini asla belirtmedi; ancak burada insan görüşüne özel olarak uyarlanmış bir algoritma için bakınız .
BlueRaja - Danny Pflughoeft

+ Bu benim ilk fikrim.
ST3

9
Burada başka bir sorun 255, 0, 0, 0, 255, 0 ile 0, 0, 255 olduğu gibi aynı mesafedir.

27

aslında birkaç ay önce aynı yolda yürüdüm. sorusuna mükemmel bir cevap yoktur ( burada birkaç kez sorulmuştur ), ancak sqrt (rr) vb. cevaptan daha karmaşık ve her türlü alternatif renk uzayına geçmeden doğrudan RGB ile implant yapmak daha kolaydır. Burada oldukça karmaşık gerçek formülün düşük maliyetli bir yaklaşımı olan bu formülü buldum (renk W3C olan CIE tarafından, bu bitmemiş bir görev olduğundan, orada daha eski ve daha basit renk farkı denklemlerini bulabilirsiniz). iyi şanslar

Düzenleme: Posterity için, ilgili C kodu şöyledir:

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

bu yöntem benim için çalıştı. Renk adları listesinden en yakın rengi bulmama yardımcı oldu.
faisalbhagat

23

Bir renk değerinin birden fazla boyutu vardır, bu nedenle iki rengi karşılaştırmanın gerçek bir yolu yoktur. Kullanım durumunuz için renklerin anlamını ve böylece bunları en iyi nasıl karşılaştıracağınızı belirlemelisiniz.

Büyük olasılıkla, kırmızı / yeşil / mavi bileşenlerin aksine renklerin ton, doygunluk ve / veya açıklık özelliklerini karşılaştırmak istersiniz. Bunları nasıl karşılaştırmak istediğinizi bulmakta sorun yaşıyorsanız, birkaç örnek renk alın ve zihinsel olarak karşılaştırın, sonra neden benzer / farklı olduklarını kendinize açıklamaya / açıklamaya çalışın.

Renklerin hangi özelliklerini / bileşenlerini karşılaştırmak istediğinizi öğrendikten sonra, bu bilgileri bir renkten nasıl çıkaracağınızı anlamanız gerekir.

Büyük olasılıkla, rengi ortak RedGreenBlue sunumundan HueSaturationLightness'e dönüştürmeniz ve ardından böyle bir şeyi hesaplamanız gerekecektir.

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

Bu örnek size renklerin gradyanı / tonunun birbirinden ne kadar uzak olduğunu gösteren basit bir skaler değer verecektir.

Bkz . Wikipedia'da HSL ve HSV .


2
Bu konularla ilgili derslerimden hatırladığım şeylerden, görüntüyü HSV / HSL yerine Lab renk alanına dönüştürürdüm. Bunu seçmek için bir sebep var mı?
Voo

Hayır! RGB ve HSL en çok aşina olduğum şeyler, bu yüzden HSL'yi seçtim ve "varsayılan" RGB'nin tek seçenek olmadığı fikrinin altını çizdim - gerçekten uygulamaya bağlı. Lab renk alanı hakkında bilgi verdiğiniz için teşekkür ederiz.
Supr

1
Zaten sana +1 verdim çünkü buradaki temel prensip "doğru" cevaptır (algılanan farkı tekdüze bir şekilde işleyen ve sonra karşılaştırma yapan renk uzayına dönüştürün). Hangi alanın en iyi olacağından emin değilim - tüm bu farklı renk uzayları cehennem imo olarak kafa karıştırıcı;)
Voo

21

İki Colornesneniz varsa c1ve c2her bir RGB değerini c1ile olan değerlerini karşılaştırabilirsiniz 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());

Bu değerleri sadece fark doygunluk miktarına (255) bölebilirsiniz ve ikisi arasındaki farkı elde edersiniz.

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

Bundan sonra, ortalama renk farkını yüzde olarak bulabilirsiniz.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Bu da c1ve arasında yüzde olarak bir fark verir c2.


2 küçük şey daha: pctDiffRed = diffRed / 255;Bir yerde bir şamandıra atmadığınız sürece <b> 1 </b> size 0 verecek. <b> 2 </b> Yüzde elde etmek için bir yerlerde 100 ile çarpmanız gerekir.
vaughandroid

18
Bu, en iyi "görünür" farkı vermeyebilir, çünkü insan gözü renk değişimlerini farklı algılar. Bununla birlikte, tam olarak aradığı şey olduğunu tahmin ediyorum, çünkü muhtemelen algılanan bir farktan ziyade eşit olarak ölçülebilir bir fark arıyor. Sadece bu konuyla ilgiliyse dikkate almam gerektiğini düşündüm.
Beska

14

İki rengi insan algısına göre karşılaştırmanın en iyi yöntemlerinden biri CIE76'dır. Fark Delta-E olarak adlandırılır. 1'den az olduğunda, insan gözü farkı tanıyamaz.

CIE76 karşılaştırma yöntemlerini içeren harika renk yardımcı programı sınıfı ColorUtils (aşağıdaki kod) vardır. Zürih Üniversitesi Daniel Strebel tarafından yazılmıştır.

ColorUtils.class yöntemini kullanıyorum:

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

r1, g1, b1 - İlk rengin RGB değerleri

r2, g2, b2 - RGB değerleri karşılaştırmak istediğiniz ikinci renge

Android ile çalışıyorsanız, bu değerleri aşağıdaki gibi alabilirsiniz:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


Zürih Üniversitesi Daniel Strebel'den ColorUtils.class:

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

yukarıdaki kodun rgb2lab'de bir hatası vardır: 12'ye bölme, r, g ve b dönüşümlerinde 12,92 bölme ile değiştirilmelidir. aksi takdirde işlev r = 0.04045'te sürekli değildir
John Smith

10

Supr'ınkine benzer olsa da, başka bir cevap - sadece farklı bir renk alanı.

Mesele şu ki: İnsanlar renk farkını eşit değil, RGB renk alanı bunu görmezden geliyor. Sonuç olarak, RGB renk uzayını kullanır ve sadece 2 renk arasındaki öklid mesafesini hesaplarsanız, matematiksel olarak kesinlikle doğru olan, ancak insanların size söyleyeceği şeyle çakışmayacak bir fark elde edebilirsiniz.

Bu bir sorun olmayabilir - fark bu kadar büyük değil bence, ama bu "daha iyi" çözmek istiyorsanız, RGB renklerinizi yukarıdaki sorunu önlemek için özel olarak tasarlanmış bir renk alanına dönüştürmelisiniz. Birkaç model var, önceki modellerden gelişmeler (bu, insan algısına dayandığından, deneysel verilere dayanarak "doğru" değerleri ölçmemiz gerekiyor). Bunu yapmak için biraz karmaşık olmasına rağmen en iyi olacağını düşündüğüm Lab renk alanı var. Daha basit olan CIE XYZ olurdu .

Burada , biraz deneme yapabilmeniz için farklı renk uzayları arasında dönüştürmek üzere formülleri listeleyen bir site var .


3

Aşağıdaki tüm yöntemler 0-100 arasında bir ölçek ile sonuçlanır.

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

En iyi yol deltaE. DeltaE, renklerin farkını gösteren bir sayıdır. Eğer delta <1 ise, fark insan gözüyle tanınamaz. Ben rgb laboratuvara dönüştürmek ve daha sonra delta e hesaplamak için tuval ve js bir kod yazdım. Bu örnekte kod, LAB1 olarak kaydettiğim temel renkle farklı renkteki pikselleri tanıyor. ve farklıysa bu pikselleri kırmızı yapar. Increae ile renk farkının hassasiyetini artırabilir veya azaltabilir veya kabul edilebilir delta aralığını azaltabilirsiniz. Bu örnekte yazdığım satırda deltaE için 10 atadım (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
Tamsayı bölümlerinizden biraz endişeliyim. 1/3ve 16/116ikisi de değerlendirir 0, ki bu kesinlikle istediğiniz şey değildir. Muhtemelen algoritmanız doğrudur, ancak kodunuz kesinlikle doğru değildir.
Dawood ibn Kareem

CIE-LAB dE94'ü açıklıyorsunuz. Delta E, Öklid'deki değişim anlamına gelir. Yani standart Lab renk uzayında, çok standart öklid mesafesi formülünüz tarafından verilen Öklid mesafesi. Delta E'nin (76, 94, 2000) (tekstiller ve benzerleri için kullanılan Delta E, CMC de vardır) modifikasyonları Lab renk uzayındaki pozisyonlar arasındaki farklı mesafe formülleridir. Laboratuarın kodu her birinde aynıdır, renk farkının kodu değildir. . Kısacası, Delta E, buna denir.
2'de Tatarize

2

Yalnızca RGB kullanan basit bir yöntem

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)

Bunu bir süredir kullandım ve çoğu amaç için yeterince iyi çalışıyor.


Yukarıdaki formülü kullanarak, mesafe için değer aralığı nedir
Aman Aggarwal

bu Öklid renk farkı yaklaşımına oldukça yakındır. Ben hesaplama hızlandırmak için kök bileşeni atlıyor tahmin ediyorum, bu yüzden 0 ila 100 ^ 3 arasında bir aralıktır. 100'e normalleştirmek istiyorsanız, gücüne mesafe verin1/3
Daniel

2

Bunu androidimde kullandım ve RGB alanı önerilmese de tatmin edici görünüyor:

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

Sonra benzerlik yüzdesi elde etmek için aşağıdakileri kullandım:

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

Yeterince iyi çalışıyor.


2

LAB renk alanı, HSV karşılaştırmaları gibi çeşitli yöntemleri denedim ve parlaklık bu amaç için oldukça iyi çalıştı.

İşte Python sürümü

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

Sana vereceğim

0.0687619047619048

Kökeni nedir ImageColor? düzenlemekfrom PIL import ImageColor
Buldum

Parlaklık bir rengin parlaklığı değil midir? Yani bu durumda, parlaklık aynı olduğu sürece yeşil, mavi ve kırmızı bir rengin farklı olduğu bildirilmiyordu?
Peter B.

1

Sonunda tüm bir görüntüyü analiz etmek istediğini umuyorum, değil mi? Böylece, kimlik rengi matrisindeki en küçük / en yüksek farkı kontrol edebilirsiniz.

Grafikleri işlemek için çoğu matematik işlemi matris kullanır, çünkü bunları kullanan olası algoritmalar klasik noktadan noktaya mesafe ve karşılaştırma hesaplamalarından daha hızlıdır. (örneğin DirectX, OpenGL, ... kullanan işlemler için)

Sanırım buradan başlamalısın:

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

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

... ve Beska'nın daha önce yorumladığı gibi:

Bu, en iyi "görünür" farkı vermeyebilir ...

Bu da görüntüleri işliyorsanız algoritmanızın "benzer" tanımınıza bağlı olduğu anlamına gelir.


1

Kotlin versiyonuyla yüzde kaçını eşleştirmek istiyorsunuz.

İsteğe bağlı bağımsız değişken yüzde ile yöntem çağrısı

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

Yöntem gövdesi

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

İnsanların gördüğü şekilde karşılaştırabilmek için herhangi bir RGB rengini Lab renk alanına dönüştürmeniz gerekir. Aksi takdirde, çok garip yollarla 'eşleşen' RGB renkleri elde edersiniz.

Renk Farklılıkları hakkındaki wikipedia bağlantısı , yıllar boyunca tanımlanan çeşitli Lab renk alanı fark algoritmalarına bir giriş sağlar. İki laboratuvar renginin Öklid mesafesini kontrol eden en basit, çalışır, ancak birkaç kusuru vardır.

Uygun şekilde OpenIMAJ projesinde daha gelişmiş CIEDE2000 algoritmasının Java uygulaması vardır . İki Lab renk setinizi sağlayın ve size tek bir mesafe değeri verecektir.


0

Renkleri karşılaştırmanın tek "doğru" yolu, bunu CIELab veya CIELuv'daki deltaE ile yapmaktır.

Ancak birçok uygulama için bunun yeterince iyi bir yaklaşım olduğunu düşünüyorum:

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

Renkleri karşılaştırırken ağırlıklı bir manhattan mesafesinin çok daha anlamlı olduğunu düşünüyorum. Renk primerlerinin sadece kafamızda olduğunu unutmayın. Fiziksel bir önemi yoktur. CIELab ve CIELuv, renk algımızdan istatistiksel olarak modellenmiştir.


0

Hızlı ve kirli için yapabilirsin

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

renkleri ölçmek için tamsayı bölme yöntemini kullanır.


0

Swift 5 Cevap

Bu konuyu bir Swift sürümüne ihtiyacım olduğu için bu konuyu buldum. Kimse çözümle cevap vermediği gibi, işte benim:

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

Kullanımı:

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

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

Benzer renkleri döndürmek için% 10'dan daha az fark ayarladım, ancak bunu kendiniz özelleştirebilirsiniz.


0

ColorUtils API için Android RGBToHSL: İki int argb rengim vardı (color1, color2) ve iki renk arasında mesafe / fark almak istedim. İşte yaptığım şey;

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

Sonra iki renk arasındaki mesafeyi bulmak için aşağıdaki kodu kullandım.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.