Bagaimana membandingkan dua warna untuk kesamaan / perbedaan


171

Saya ingin merancang sebuah program yang dapat membantu saya menilai antara 5 warna yang telah ditentukan yang mana yang lebih mirip dengan warna variabel, dan dengan persentase berapa. Masalahnya adalah saya tidak tahu bagaimana melakukannya secara manual langkah demi langkah. Jadi lebih sulit untuk memikirkan suatu program.

Lebih detail: Warnanya dari foto tabung dengan gel yang warnanya berbeda. Saya memiliki 5 tabung dengan warna berbeda yang masing-masing mewakili 1 dari 5 level. Saya ingin mengambil foto dari sampel lain dan di komputer menilai ke tingkat mana sampel itu dimiliki dengan membandingkan warna, dan saya ingin tahu itu dengan persentase perkiraan juga. Saya ingin program yang melakukan sesuatu seperti ini: http://www.colortools.net/color_matcher.html

Jika Anda dapat memberi tahu saya langkah apa yang harus diambil, bahkan jika itu adalah hal yang harus saya pikirkan dan lakukan secara manual. Itu akan sangat membantu.


1
Saya membuat perubahan kecil pada teks, mengubah kata Portugis menjadi apa yang saya pikir setara dengan bahasa Inggris yang benar ... mengubahnya kembali jika saya salah.
Beska

13
Ada artikel wikipedia tentang perbedaan warna: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Ini harus menarik: stevehanov.ca/blog/index.php?id=116 Ini mengeksplorasi komputasi perbedaan dalam tiga model warna yang berbeda.
Vlad

Hai @OcasoProtal, itu tautan yang bagus terima kasih sudah berbagi. Dan untuk OP, pertanyaan menarik.
Persepsi

Cobalah untuk meminimalkan variabilitas foto yang potensial juga ... lebih detail dalam jawaban di bawah ini.
Beska

Jawaban:


130

Lihat artikel Wikipedia tentang Perbedaan Warna untuk arahan yang tepat. Pada dasarnya, Anda ingin menghitung metrik jarak dalam beberapa ruang warna multidimensi. Tetapi RGB tidak "seragam secara perseptual", jadi metrik jarak Euclidean RGB Anda yang disarankan oleh Vadim tidak akan cocok dengan jarak yang dirasakan manusia antara warna. Sebagai permulaan, L a b * dimaksudkan untuk menjadi ruang warna yang secara seragam seragam, dan metrik deltaE biasanya digunakan. Tetapi ada lebih banyak ruang warna yang lebih halus dan formula delta yang lebih halus yang lebih dekat dengan pencocokan persepsi manusia.

Anda harus mempelajari lebih lanjut tentang ruang warna dan iluminan untuk melakukan konversi. Tetapi untuk rumus cepat yang lebih baik daripada metrik Euclidean RGB, lakukan saja ini: asumsikan bahwa nilai RGB Anda berada di ruang warna sRGB, temukan rumus konversi sRGB ke L a b *, ubah warna sRGB Anda menjadi L a b *, dan hitung deltaE antara dua nilai L a b * Anda. Ini tidak mahal secara komputasi, itu hanya beberapa rumus nonlinier dan beberapa mengalikan dan menambahkan.


11
+1 untuk "deltaE", itulah metode perbandingan yang paling standar dan ada adaptasi rumus deltaE untuk berbagai kasus penggunaan.
Martin Hennings

9
Anda dapat menemukan rumus konversi di sini: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Atau, jika Anda bekerja di Ruby, periksa colorpermata yang mengimplementasikan deltaE di antara operasi warna lainnya.
Mike Jarema

Berikut adalah intisari untuk implementasi di atas dalam Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Hanya sebuah ide yang pertama kali terlintas di pikiran saya (maaf jika bodoh). Tiga komponen warna dapat diasumsikan koordinat 3D titik dan kemudian Anda dapat menghitung jarak antar titik.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

Jarak antar warna adalah

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

Persentase adalah

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

28
Jika kita menggunakan ruang warna RGB perbedaan antara 2 warna tidak sama dengan bagaimana manusia melihat perbedaannya. Tapi ya ide dasarnya sama di mana-mana - kita hanya perlu memetakannya ke ruang warna lain (lab saya pikir)
Voo

6
@ Voo: Saya setuju, HSV / HSL / LAB akan menjadi ruang warna yang jauh lebih baik daripada RGB untuk pencocokan kesamaan berdasarkan jarak.
Jon Purdy

4
Ini adalah cara yang baik untuk memberi tahu Anda betapa berbedanya dua warna, tetapi melakukan pekerjaan yang buruk untuk memberi tahu Anda betapa berbedanya warna-warna tersebut. Mata manusia jauh dari sempurna: kita lebih sensitif terhadap hijau daripada merah atau biru, persepsi kecerahan kita adalah logritmik, dll. OP tidak pernah menentukan yang dia inginkan; tetapi lihat di sini untuk algoritma yang dirancang khusus untuk penglihatan manusia.
BlueRaja - Danny Pflughoeft

+ Ini adalah ide pertamaku juga.
ST3

9
Masalah lain di sini adalah 255, 0, 0 adalah jarak yang sama dari 0, 255, 0 sebagaimana 0, 0, 255.

27

sebenarnya saya berjalan di jalan yang sama beberapa bulan yang lalu. tidak ada jawaban sempurna untuk pertanyaan (yang ditanyakan di sini beberapa kali) tetapi ada satu yang lebih canggih daripada jawaban sqrt (rr) dll dan lebih mudah untuk diterapkan secara langsung dengan RGB tanpa berpindah ke semua jenis ruang warna alternatif. Saya menemukan rumus ini di sini yang merupakan perkiraan biaya rendah dari rumus nyata yang cukup rumit (oleh CIE yang merupakan W3C warna, karena ini adalah pencarian yang belum selesai, Anda dapat menemukan persamaan perbedaan warna yang lebih tua dan lebih sederhana di sana). semoga berhasil

Sunting: Untuk anak cucu, inilah kode C yang relevan:

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

metode ini berhasil untuk saya. Ini membantu saya menemukan warna terdekat dari daftar nama warna.
faisalbhagat

23

Nilai warna memiliki lebih dari satu dimensi, jadi tidak ada cara intrinsik untuk membandingkan dua warna. Anda harus menentukan untuk kasus penggunaan Anda arti warna dan dengan demikian cara terbaik untuk membandingkannya.

Kemungkinan besar Anda ingin membandingkan rona, saturasi, dan / atau sifat warna yang terang yang berlawanan dengan komponen merah / hijau / biru. Jika Anda mengalami kesulitan mencari tahu bagaimana Anda ingin membandingkannya, ambil beberapa pasang warna sampel dan bandingkan secara mental, maka cobalah untuk membenarkan / menjelaskan kepada diri sendiri mengapa mereka mirip / berbeda.

Setelah Anda mengetahui properti / komponen warna yang ingin Anda bandingkan, maka Anda perlu mencari cara untuk mengekstrak informasi itu dari suatu warna.

Kemungkinan besar Anda hanya perlu mengubah warna dari representasi RedGreenBlue yang umum menjadi HueSaturationLightness, dan kemudian menghitung sesuatu seperti

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

Contoh ini akan memberi Anda nilai skalar sederhana yang menunjukkan seberapa jauh gradien / rona warna satu sama lain.

Lihat HSL dan HSV di Wikipedia .


2
Dari hal-hal yang saya ingat dari kuliah saya tentang hal-hal ini saya akan mengubah gambar menjadi ruang warna Lab dan bukan HSV / HSL sekalipun. Adakah alasan untuk memilih yang itu?
Voo

Nggak. RGB dan HSL adalah yang paling saya kenal, jadi saya memilih HSL hanya untuk menggarisbawahi gagasan bahwa "default" RGB bukan satu-satunya pilihan - itu benar-benar tergantung pada aplikasi. Terima kasih telah memberi tahu saya tentang ruang warna Lab.
Supr

1
Saya memberi Anda +1 karena prinsip dasar di sini adalah jawaban "benar" (konversikan dalam ruang warna yang menangani perbedaan yang dirasakan secara seragam lalu lakukan perbandingan). Saya tidak yakin ruang mana yang akan menjadi yang terbaik - semua ruang warna yang berbeda ini membingungkan, imo;)
Voo

21

Jika Anda memiliki dua Colorobjek c1dan c2, Anda dapat membandingkan setiap nilai RGB dari c1dengan 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());

Nilai-nilai yang bisa Anda bagi dengan jumlah perbedaan saturasi (255), dan Anda akan mendapatkan perbedaan di antara keduanya.

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

Setelah itu Anda bisa menemukan perbedaan warna rata-rata dalam persentase.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Yang akan memberi Anda perbedaan persentase antara c1dan c2.


2 hal kecil lagi: <b> 1 </b> pctDiffRed = diffRed / 255;akan memberi Anda 0 kecuali jika Anda melakukan float di suatu tempat. <b> 2 </b> Anda harus mengalikan 100 di suatu tempat untuk mendapatkan persentase.
vaughandroid

18
Ini mungkin tidak memberikan perbedaan "terlihat" terbaik, karena mata manusia merasakan perubahan warna secara berbeda. Yang sedang berkata, saya menduga ini adalah persis apa yang dia cari, karena dia mungkin mencari perbedaan yang sama-sama terukur daripada perbedaan yang dipersepsikan. Hanya berpikir saya akan keluar di sini sebagai sesuatu untuk dipertimbangkan jika itu relevan.
Beska

14

Salah satu metode terbaik untuk membandingkan dua warna dengan persepsi manusia adalah CIE76. Perbedaannya disebut Delta-E. Ketika kurang dari 1, mata manusia tidak bisa mengenali perbedaannya.

Ada kelas utilitas warna yang luar biasa, ColorUtils (kode di bawah), yang mencakup metode perbandingan CIE76. Ini ditulis oleh Daniel Strebel, University of Zurich.

Dari ColorUtils.class saya menggunakan metode ini:

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

r1, g1, b1 - Nilai RGB dari warna pertama

r2, g2, b2 - nilai RGB dari warna kedua yang ingin Anda bandingkan

Jika Anda bekerja dengan Android, Anda bisa mendapatkan nilai-nilai ini seperti ini:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


Kelas ColorUtils.class oleh Daniel Strebel, Universitas Zurich:

import android.graphics.Color;

public class ColorUtil {
public static int argb(int R, int G, int B) {
    return argb(Byte.MAX_VALUE, R, G, B);
}

public static int argb(int A, int R, int G, int B) {
    byte[] colorByteArr = {(byte) A, (byte) R, (byte) G, (byte) B};
    return byteArrToInt(colorByteArr);
}

public static int[] rgb(int argb) {
    return new int[]{(argb >> 16) & 0xFF, (argb >> 8) & 0xFF, argb & 0xFF};
}

public static int byteArrToInt(byte[] colorByteArr) {
    return (colorByteArr[0] << 24) + ((colorByteArr[1] & 0xFF) << 16)
            + ((colorByteArr[2] & 0xFF) << 8) + (colorByteArr[3] & 0xFF);
}

public static int[] rgb2lab(int R, int G, int B) {
    //http://www.brucelindbloom.com

    float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr;
    float Ls, as, bs;
    float eps = 216.f / 24389.f;
    float k = 24389.f / 27.f;

    float Xr = 0.964221f;  // reference white D50
    float Yr = 1.0f;
    float Zr = 0.825211f;

    // RGB to XYZ
    r = R / 255.f; //R 0..1
    g = G / 255.f; //G 0..1
    b = B / 255.f; //B 0..1

    // assuming sRGB (D65)
    if (r <= 0.04045)
        r = r / 12;
    else
        r = (float) Math.pow((r + 0.055) / 1.055, 2.4);

    if (g <= 0.04045)
        g = g / 12;
    else
        g = (float) Math.pow((g + 0.055) / 1.055, 2.4);

    if (b <= 0.04045)
        b = b / 12;
    else
        b = (float) Math.pow((b + 0.055) / 1.055, 2.4);


    X = 0.436052025f * r + 0.385081593f * g + 0.143087414f * b;
    Y = 0.222491598f * r + 0.71688606f * g + 0.060621486f * b;
    Z = 0.013929122f * r + 0.097097002f * g + 0.71418547f * b;

    // XYZ to Lab
    xr = X / Xr;
    yr = Y / Yr;
    zr = Z / Zr;

    if (xr > eps)
        fx = (float) Math.pow(xr, 1 / 3.);
    else
        fx = (float) ((k * xr + 16.) / 116.);

    if (yr > eps)
        fy = (float) Math.pow(yr, 1 / 3.);
    else
        fy = (float) ((k * yr + 16.) / 116.);

    if (zr > eps)
        fz = (float) Math.pow(zr, 1 / 3.);
    else
        fz = (float) ((k * zr + 16.) / 116);

    Ls = (116 * fy) - 16;
    as = 500 * (fx - fy);
    bs = 200 * (fy - fz);

    int[] lab = new int[3];
    lab[0] = (int) (2.55 * Ls + .5);
    lab[1] = (int) (as + .5);
    lab[2] = (int) (bs + .5);
    return lab;
}

/**
 * Computes the difference between two RGB colors by converting them to the L*a*b scale and
 * comparing them using the CIE76 algorithm { http://en.wikipedia.org/wiki/Color_difference#CIE76}
 */
public static double getColorDifference(int a, int b) {
    int r1, g1, b1, r2, g2, b2;
    r1 = Color.red(a);
    g1 = Color.green(a);
    b1 = Color.blue(a);
    r2 = Color.red(b);
    g2 = Color.green(b);
    b2 = Color.blue(b);
    int[] lab1 = rgb2lab(r1, g1, b1);
    int[] lab2 = rgb2lab(r2, g2, b2);
    return Math.sqrt(Math.pow(lab2[0] - lab1[0], 2) + Math.pow(lab2[1] - lab1[1], 2) + Math.pow(lab2[2] - lab1[2], 2));
}
}

kode di atas memiliki kesalahan dalam rgb2lab: pembagian dengan 12 harus diganti dengan pembagian dengan 12,92 dalam konversi r, g dan b. jika tidak, fungsi tidak kontinu pada r = 0,04045
John Smith

10

Hanya jawaban lain, meskipun mirip dengan yang Supr - hanya ruang warna yang berbeda.

Masalahnya adalah: Manusia merasakan perbedaan warna tidak seragam dan ruang warna RGB mengabaikan hal ini. Akibatnya jika Anda menggunakan ruang warna RGB dan hanya menghitung jarak euclidean antara 2 warna, Anda mungkin mendapatkan perbedaan yang secara matematis benar, tetapi tidak akan sesuai dengan apa yang manusia katakan kepada Anda.

Ini mungkin bukan masalah - perbedaannya tidak terlalu besar saya pikir, tetapi jika Anda ingin menyelesaikan ini "lebih baik" Anda harus mengubah warna RGB Anda menjadi ruang warna yang dirancang khusus untuk menghindari masalah di atas. Ada beberapa yang, perbaikan dari model sebelumnya (karena ini didasarkan pada persepsi manusia kita perlu mengukur nilai "benar" berdasarkan data eksperimen). Ada ruang warna Lab yang saya pikir akan menjadi yang terbaik meskipun agak rumit untuk mengubahnya. Simpler akan menjadi CIE XYZ .

Berikut adalah situs yang mencantumkan rumus untuk mengkonversi antara ruang warna yang berbeda sehingga Anda dapat sedikit bereksperimen.


3

Semua metode di bawah ini menghasilkan skala 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

Cara terbaik adalah deltaE. DeltaE adalah angka yang menunjukkan perbedaan warna. Jika delta <1 maka perbedaannya tidak dapat dikenali oleh mata manusia. Saya menulis kode di kanvas dan js untuk mengubah rgb ke lab dan kemudian menghitung delta e. Pada contoh ini kode ini mengenali piksel yang memiliki warna berbeda dengan warna dasar yang saya simpan sebagai LAB1. dan jika berbeda maka piksel tersebut menjadi merah. Anda dapat menambah atau mengurangi sensitivitas perbedaan warna dengan menambah atau mengurangi kisaran delta yang dapat diterima. Dalam contoh ini saya menetapkan 10 untuk deltaE di baris yang saya tulis (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
Saya sedikit khawatir dengan beberapa divisi integer Anda. 1/3dan 16/116keduanya mengevaluasi 0, yang hampir pasti bukan yang Anda inginkan. Mungkin algoritme Anda benar, tetapi kode Anda tentu saja tidak.
Dawood ibn Kareem

Anda menggambarkan CIE-LAB dE94. Delta E berarti perubahan Euclidean. Artinya dalam ruang warna Lab standar, jarak Euclidean diberikan oleh rumus jarak euclidean Anda yang sangat standar. Sedangkan modifikasi Delta E, yaitu 76, 94, 2000 (ada juga Delta E, CMC yang digunakan untuk tekstil dan sejenisnya) adalah formula jarak yang berbeda antara posisi dalam ruang warna Lab. Kode untuk Lab adalah sama di masing-masing, kode untuk perbedaan warna tidak. . Singkatnya, Delta E, bukan yang disebut.
Tatarize

2

Metode sederhana yang hanya menggunakan RGB adalah

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)

Saya telah menggunakan ini untuk sementara waktu sekarang, dan itu berfungsi cukup baik untuk sebagian besar tujuan.


Menggunakan rumus di atas, berapa kisaran nilai untuk jarak
Aman Aggarwal

ini cukup dekat dengan perkiraan perbedaan warna Euclidean. Saya menduga itu melewatkan komponen root untuk mempercepat perhitungan, jadi itu berkisar dari 0 hingga 100 ^ 3. Jika Anda ingin menormalkan ke 100, lakukan jarak ke kekuasaan1/3
Daniel

2

Saya menggunakan ini di android saya dan tampaknya memuaskan meskipun ruang RGB tidak disarankan:

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

Lalu saya menggunakan yang berikut ini untuk mendapatkan persen kesamaan:

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

Ini bekerja dengan cukup baik.


2

Saya telah mencoba berbagai metode seperti ruang warna LAB, perbandingan HSV dan saya telah menemukan bahwa luminositas bekerja cukup baik untuk tujuan ini.

Ini adalah versi 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))

Akan memberimu

0.0687619047619048

Apa asalnya ImageColor? sunting Saya menemukan, itufrom PIL import ImageColor
ademar111190

Bukankah luminositas kecerahan warna? Jadi dalam hal ini warna hijau, biru dan merah tidak akan dilaporkan berbeda asalkan kecerahannya sama?
Peter B.

1

Saya berharap Anda ingin menganalisis seluruh gambar pada akhirnya, bukan? Jadi Anda bisa memeriksa perbedaan terkecil / tertinggi dengan matriks warna identitas.

Sebagian besar operasi matematika untuk memproses grafik menggunakan matriks, karena algoritma yang mungkin menggunakannya seringkali lebih cepat daripada jarak titik demi titik klasik dan perhitungan perbandingan. (mis. untuk operasi menggunakan DirectX, OpenGL, ...)

Jadi saya pikir Anda harus mulai dari sini:

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

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

... dan seperti yang sudah dikomentari Beska di atas:

Ini mungkin tidak memberikan perbedaan "terlihat" terbaik ...

Yang juga berarti bahwa algoritme Anda bergantung pada definisi "mirip dengan" Anda jika Anda memproses gambar.


1

Versi Kotlin dengan berapa persen yang ingin Anda cocokkan.

Panggilan metode dengan argumen opsional opsional

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

Metode tubuh

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

Anda harus mengubah warna RGB menjadi ruang warna Lab untuk dapat membandingkannya dengan cara yang dilihat manusia. Kalau tidak, Anda akan mendapatkan warna RGB yang 'cocok' dengan beberapa cara yang sangat aneh.

Tautan wikipedia pada Perbedaan Warna memberi Anda intro ke berbagai algoritma perbedaan ruang warna Lab yang telah ditentukan selama bertahun-tahun. Yang paling sederhana yang hanya memeriksa jarak Euclidian dari dua warna lab, bekerja tetapi memiliki beberapa kekurangan.

Mudahnya ada implementasi Java dari algoritma CIEDE2000 yang lebih canggih dalam proyek OpenIMAJ . Berikan dua set warna Lab Anda dan itu akan memberi Anda nilai jarak tunggal.


0

Satu-satunya cara yang "tepat" untuk membandingkan warna adalah melakukannya dengan deltaE di CIELab atau CIELuv.

Tetapi untuk banyak aplikasi saya pikir ini perkiraan yang cukup baik:

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

Saya pikir jarak manhattan tertimbang jauh lebih masuk akal ketika membandingkan warna. Ingatlah bahwa pemilihan warna hanya ada di kepala kita. Mereka tidak memiliki signifikansi fisik apa pun. CIELab dan CIELuv dimodelkan secara statistik dari persepsi kami tentang warna.


0

Agar cepat dan kotor, Anda bisa melakukannya

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

memanfaatkan pembagian integer untuk mengukur warna.


0

Swift 5 Answer

Saya menemukan utas ini karena saya memerlukan versi Swift dari pertanyaan ini. Karena tidak ada yang menjawab dengan solusinya, inilah milik saya:

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

Pemakaian:

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

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

Saya menetapkan kurang dari 10% perbedaan untuk mengembalikan warna yang serupa, tetapi Anda dapat menyesuaikan sendiri.


0

Android untuk ColorUtils API RGBToHSL: Saya punya dua warna argb int (color1, color2) dan saya ingin mendapatkan jarak / perbedaan di antara dua warna. Inilah yang saya lakukan;

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

Kemudian saya menggunakan kode di bawah ini untuk menemukan jarak antara dua warna.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.