Làm thế nào để so sánh hai màu cho sự tương đồng / khác biệt


171

Tôi muốn thiết kế một chương trình có thể giúp tôi đánh giá giữa 5 màu được xác định trước, màu nào giống với màu thay đổi hơn và với tỷ lệ bao nhiêu. Vấn đề là tôi không biết cách làm điều đó bằng tay từng bước một. Vì vậy, nó thậm chí còn khó khăn hơn để nghĩ về một chương trình.

Thêm chi tiết: Màu sắc là từ hình ảnh của các ống với gel có màu khác nhau. Tôi có 5 ống với màu sắc khác nhau, mỗi ống là đại diện cho 1 trong 5 cấp độ. Tôi muốn chụp ảnh các mẫu khác và trên máy tính để đánh giá mức độ của mẫu đó bằng cách so sánh màu sắc và tôi muốn biết rằng với tỷ lệ xấp xỉ cũng vậy. Tôi muốn một chương trình thực hiện một cái gì đó như thế này: http://www.colortools.net/color_matcher.html

Nếu bạn có thể cho tôi biết những bước cần thực hiện, ngay cả khi chúng là những thứ để tôi suy nghĩ và làm thủ công. Nó sẽ rất hữu ích.


1
Tôi đã thực hiện một thay đổi nhỏ cho văn bản, thay đổi một từ tiếng Bồ Đào Nha thành những gì tôi nghĩ là tương đương với tiếng Anh chính xác ... thay đổi lại nếu tôi sai.
Beska

13
Có một bài viết trên wikipedia về sự khác biệt màu sắc: en.wikipedia.org/wiki/Color_difference
Ocaso Protal

4
Điều này sẽ rất thú vị: stevehanov.ca/blog/index.php?id=116 Nó khám phá tính toán sự khác biệt trong ba mô hình màu khác nhau.
Vlad

Xin chào @OcasoProtal, đó là một liên kết tuyệt vời cảm ơn vì đã chia sẻ. Và với OP, câu hỏi thú vị.
Nhận thức

Cố gắng giảm thiểu bất kỳ độ mờ ảnh tiềm năng nào cũng như ... chi tiết hơn trong câu trả lời dưới đây.
Beska

Câu trả lời:


130

Xem bài viết của Wikipedia về Sự khác biệt màu sắc cho các khách hàng tiềm năng phù hợp. Về cơ bản, bạn muốn tính toán một thước đo khoảng cách trong một số không gian màu đa chiều. Nhưng RGB không "đồng nhất về mặt nhận thức", do đó, thước đo khoảng cách Euclide RGB của bạn được đề xuất bởi Vadim sẽ không khớp với khoảng cách cảm nhận của con người giữa các màu. Để bắt đầu, L a b * được dự định là không gian màu thống nhất về mặt cảm nhận và số liệu deltaE thường được sử dụng. Nhưng có nhiều không gian màu tinh tế hơn và các công thức deltaE tinh tế hơn, gần với nhận thức của con người hơn.

Bạn sẽ phải tìm hiểu thêm về không gian màu và chất chiếu sáng để thực hiện chuyển đổi. Nhưng đối với một công thức nhanh chóng mà là tốt hơn so với Euclide RGB số liệu, chỉ cần làm như sau: giả sử rằng giá trị RGB của bạn trong không gian màu sRGB, tìm sRGB để L một b * công thức chuyển đổi, chuyển đổi màu sắc sRGB của bạn để L một b *, và tính deltaE giữa hai giá trị L a b * của bạn. Nó không đắt tiền về mặt tính toán, nó chỉ là một số công thức phi tuyến và một số phép nhân và bổ sung.


11
+1 cho "deltaE", đó là phương pháp so sánh được tiêu chuẩn hóa nhất và có sự điều chỉnh của công thức deltaE cho các trường hợp sử dụng khác nhau.
Martin Hennings

9
Bạn có thể tìm thấy các công thức chuyển đổi tại đây: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Hoặc, nếu bạn đang làm việc trong Ruby, hãy xem colorđá quý thực hiện deltaE trong số các hoạt động màu khác.
Mike Jarema

Đây là ý chính cho việc triển khai ở trên trong Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Chỉ là một ý tưởng đầu tiên xuất hiện trong đầu tôi (xin lỗi nếu ngu ngốc). Ba thành phần màu sắc có thể được giả định là tọa độ 3D của các điểm và sau đó bạn có thể tính khoảng cách giữa các điểm.

FE

Point1 has R1 G1 B1
Point2 has R2 G2 B2

Khoảng cách giữa các màu là

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

Tỷ lệ là

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

28
Nếu chúng ta sử dụng không gian màu RGB, sự khác biệt giữa 2 màu không giống như cách con người cảm nhận sự khác biệt. Nhưng vâng, ý tưởng cơ bản giống nhau ở mọi nơi - chúng ta chỉ cần ánh xạ nó vào một không gian màu khác (phòng thí nghiệm tôi nghĩ)
Voo

6
@Voo: Tôi đồng ý, HSV / HSL / LAB sẽ có không gian màu tốt hơn đáng kể so với (s) RGB để khớp tương tự dựa trên khoảng cách.
Jon Purdy

4
Đây là một cách tốt để cho bạn biết hai màu sắc khác nhau như thế nào, nhưng thực hiện công việc kém là cho bạn biết chúng sẽ khác nhau như thế nào. Mắt người không hoàn hảo: chúng ta nhạy cảm với màu xanh lá cây hơn màu đỏ hoặc màu xanh lam, nhận thức về độ sáng của chúng ta là logrithmic, v.v ... OP không bao giờ chỉ định mình muốn gì; nhưng xem ở đây cho một thuật toán được thiết kế đặc biệt cho thị giác của con người.
BlueRaja - Daniel Pflughoeft

+ Đó là ý tưởng đầu tiên của tôi là tốt.
ST3

9
Một vấn đề khác ở đây là 255, 0, 0 có cùng khoảng cách từ 0, 255, 0 vì nó là 0, 0, 255.

27

Thật ra tôi đã đi trên cùng một con đường vài tháng trước. không có câu trả lời hoàn hảo cho câu hỏi (đã được hỏi ở đây một vài lần) nhưng có một câu trả lời phức tạp hơn sau đó là câu trả lời sqrt (rr) vv và dễ dàng hơn để cấy trực tiếp với RGB mà không cần di chuyển đến tất cả các không gian màu thay thế. Tôi đã tìm thấy công thức này ở đây là một xấp xỉ chi phí thấp của công thức thực khá phức tạp (bởi CIE là W3C của màu sắc, vì đây không phải là một nhiệm vụ chưa hoàn thành, bạn có thể tìm thấy các phương trình khác biệt màu cũ hơn và đơn giản hơn ở đó). chúc may mắn

Chỉnh sửa: Đối với hậu thế, đây là mã C có liên quan:

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

phương pháp này hiệu quả với tôi Nó giúp tôi tìm màu gần nhất từ ​​danh sách tên màu.
faisalbhagat

23

Một giá trị màu có nhiều hơn một chiều, vì vậy không có cách nội tại để so sánh hai màu. Bạn phải xác định cho trường hợp sử dụng của bạn ý nghĩa của màu sắc và từ đó làm thế nào để so sánh chúng tốt nhất.

Nhiều khả năng bạn muốn so sánh các đặc tính màu sắc, độ bão hòa và / hoặc độ sáng của màu sắc khi được đặt với các thành phần màu đỏ / xanh lá cây / xanh dương. Nếu bạn gặp khó khăn trong việc tìm ra cách bạn muốn so sánh chúng, hãy lấy một số cặp màu mẫu và so sánh chúng về mặt tinh thần, sau đó thử tự biện minh / giải thích cho bạn tại sao chúng giống nhau / khác nhau.

Khi bạn biết thuộc tính / thành phần nào của màu bạn muốn so sánh, thì bạn cần tìm ra cách trích xuất thông tin đó từ một màu.

Nhiều khả năng bạn sẽ chỉ cần chuyển đổi màu từ đại diện RedGreenBlue phổ biến sang HueSaturationLightness, sau đó tính toán một cái gì đó như

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

Ví dụ này sẽ cung cấp cho bạn một giá trị vô hướng đơn giản cho biết độ dốc / màu của các màu cách nhau bao xa.

Xem HSL và HSV tại Wikipedia .


2
Từ những thứ tôi nhớ được từ các bài giảng của mình về những điều này, tôi sẽ chuyển đổi hình ảnh vào không gian màu Lab và không phải HSV / HSL. Bất kỳ lý do để chọn cái đó?
Voo

Không. RGB và HSL là những cái tôi quen thuộc nhất, vì vậy tôi đã chọn HSL chỉ để nhấn mạnh ý tưởng rằng RGB "mặc định" không phải là lựa chọn duy nhất - nó thực sự phụ thuộc vào ứng dụng. Cảm ơn đã cho tôi biết về không gian màu Lab.
Supr

1
Dù sao tôi cũng cho bạn +1 vì nguyên tắc cơ bản ở đây là câu trả lời "đúng" (chuyển đổi trong không gian màu xử lý sự khác biệt nhận thức một cách đồng nhất sau đó thực hiện so sánh). Tôi không chắc chắn không gian nào là tốt nhất - tất cả các không gian màu khác nhau này đều khó hiểu như địa ngục imo;)
Voo

21

Nếu bạn có hai Colorđối tượng c1c2, bạn chỉ có thể so sánh từng giá trị RGB với giá trị c1đó 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());

Những giá trị bạn có thể chỉ cần chia cho số lượng bão hòa chênh lệch (255) và bạn sẽ nhận được sự khác biệt giữa hai giá trị.

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

Sau đó, bạn chỉ có thể tìm thấy sự khác biệt màu trung bình theo tỷ lệ phần trăm.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Điều này sẽ cung cấp cho bạn một sự khác biệt về tỷ lệ phần trăm giữa c1c2.


Thêm 2 điều nhỏ nữa: <b> 1 </ b> pctDiffRed = diffRed / 255;sẽ cho bạn 0 trừ khi bạn ném vào một cái phao ở đâu đó. <b> 2 </ b> Bạn sẽ cần nhân với 100 ở đâu đó để có được tỷ lệ phần trăm.
vaughandroid

18
Điều này có thể không mang lại sự khác biệt "có thể nhìn thấy" tốt nhất, vì mắt người nhận thấy màu sắc thay đổi khác nhau. Điều đó đang được nói, tôi đoán đây chính xác là những gì cô ấy đang tìm kiếm, bởi vì cô ấy có thể đang tìm kiếm một sự khác biệt có thể định lượng bằng nhau chứ không phải là một sự khác biệt rõ ràng. Chỉ cần nghĩ rằng đây là một cái gì đó để xem xét trong trường hợp nó có liên quan.
Beska

14

Một trong những phương pháp tốt nhất để so sánh hai màu theo nhận thức của con người là CIE76. Sự khác biệt được gọi là Delta-E. Khi nó nhỏ hơn 1, mắt người không thể nhận ra sự khác biệt.

Có lớp tiện ích màu tuyệt vời ColorUtils (mã bên dưới), bao gồm các phương thức so sánh CIE76. Nó được viết bởi Daniel Strebel, Đại học Zurich.

Từ ColorUtils. Class tôi sử dụng phương thức:

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

r1, g1, b1 - Giá trị RGB của màu đầu tiên

r2, g2, b2 - Các giá trị RGB có màu thứ hai mà bạn muốn so sánh

Nếu bạn làm việc với Android, bạn có thể nhận được các giá trị như sau:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


ColorUtils. Class của Daniel Strebel, Đại học 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));
}
}

đoạn mã trên có lỗi trong rgb2lab: chia cho 12 nên được thay thế bằng chia cho 12,92 trong chuyển đổi r, g và b. mặt khác, hàm không liên tục tại r = 0,04045
John Smith

10

Chỉ là một câu trả lời khác, mặc dù nó tương tự như câu trả lời của Supr - chỉ là một không gian màu khác.

Vấn đề là: Con người nhận thấy sự khác biệt về màu sắc không đồng nhất và không gian màu RGB đang bỏ qua điều này. Kết quả là nếu bạn sử dụng không gian màu RGB và chỉ cần tính khoảng cách euclide giữa 2 màu, bạn có thể có một sự khác biệt hoàn toàn chính xác về mặt toán học, nhưng sẽ không trùng với những gì con người sẽ nói với bạn.

Đây có thể không phải là vấn đề - tôi nghĩ sự khác biệt không lớn, nhưng nếu bạn muốn giải quyết vấn đề "tốt hơn" này, bạn nên chuyển đổi màu RGB thành không gian màu được thiết kế riêng để tránh vấn đề trên. Có một số cải tiến từ các mô hình trước đó (vì điều này dựa trên nhận thức của con người, chúng ta cần đo các giá trị "chính xác" dựa trên dữ liệu thử nghiệm). Có không gian màu Lab mà tôi nghĩ sẽ là tốt nhất mặc dù hơi phức tạp để chuyển đổi nó thành. Đơn giản hơn sẽ là CIE XYZ .

Đây là một trang web liệt kê các công thức để chuyển đổi giữa các không gian màu khác nhau để bạn có thể thử nghiệm một chút.


3

Tất cả các phương pháp dưới đây dẫn đến thang điểm từ 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

Cách tốt nhất là deltaE. DeltaE là một con số cho thấy sự khác biệt của màu sắc. Nếu deltae <1 thì sự khác biệt không thể nhận ra bằng mắt người. Tôi đã viết một mã bằng canvas và js để chuyển đổi rgb sang lab và sau đó tính delta e. Trong ví dụ này, mã đang nhận ra các pixel có màu khác với màu cơ bản mà tôi đã lưu là LAB1. và sau đó nếu nó khác nhau làm cho các pixel đó màu đỏ. Bạn có thể tăng hoặc giảm độ nhạy của chênh lệch màu với tăng hoặc giảm phạm vi delta e chấp nhận được. Trong ví dụ này, tôi đã gán 10 cho deltaE trong dòng mà tôi đã viết (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
Tôi hơi lo lắng bởi một số phân chia số nguyên của bạn. 1/316/116cả hai đánh giá 0, gần như chắc chắn không phải là những gì bạn muốn. Có lẽ thuật toán của bạn là chính xác, nhưng mã của bạn chắc chắn là không.
Dawood ibn Kareem

Bạn đang mô tả CIE-LAB dE94. Delta E có nghĩa là sự thay đổi trong Euclide. Có thể nói trong không gian màu Lab tiêu chuẩn, khoảng cách Euclide được đưa ra bởi công thức khoảng cách euclide rất chuẩn của bạn. Trong khi các sửa đổi của Delta E, cụ thể là 76, 94, 2000 (cũng có Delta E, CMC được sử dụng cho hàng dệt và tương tự) là các công thức khoảng cách khác nhau giữa các vị trí trong không gian màu Lab. Mã cho Lab là giống nhau trong mỗi mã, mã cho sự khác biệt màu sắc là không. . Nói tóm lại, Delta E, không phải là cái gọi là.
Tatarize

2

Một phương pháp đơn giản chỉ sử dụng RGB là

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)

Tôi đã sử dụng cái này một thời gian rồi và nó hoạt động đủ tốt cho hầu hết các mục đích.


Sử dụng công thức trên, phạm vi của các giá trị cho khoảng cách
Aman Aggarwal

nó khá gần với xấp xỉ chênh lệch màu Euclide. Tôi đoán rằng nó đang bỏ qua thành phần gốc để tăng tốc tính toán, vì vậy đó là phạm vi từ 0 đến 100 ^ 3. Nếu bạn muốn bình thường hóa thành 100, hãy tạo khoảng cách với sức mạnh của1/3
Daniel

2

Tôi đã sử dụng điều này trong Android của tôi và nó có vẻ thỏa đáng mặc dù không nên sử dụng không gian RGB:

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

Sau đó, tôi đã sử dụng như sau để có được phần trăm tương tự:

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

Nó hoạt động đủ tốt.


2

Tôi đã thử các phương pháp khác nhau như không gian màu LAB, so sánh HSV và tôi thấy rằng độ sáng hoạt động khá tốt cho mục đích này.

Đây là phiên bản 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))

Sẽ cho bạn

0.0687619047619048

Nguồn gốc của là ImageColorgì? chỉnh sửa tôi tìm thấy, đó làfrom PIL import ImageColor
ademar111190

Độ sáng của màu không phải là độ sáng? Vì vậy, trong trường hợp này, màu xanh lá cây, xanh dương và đỏ sẽ không được báo cáo là khác nhau miễn là độ sáng là như nhau?
Peter B.

1

Tôi hy vọng bạn muốn phân tích toàn bộ hình ảnh ở cuối, phải không? Vì vậy, bạn có thể kiểm tra sự khác biệt nhỏ nhất / cao nhất đối với ma trận màu nhận dạng.

Hầu hết các phép toán để xử lý đồ họa đều sử dụng ma trận, bởi vì các thuật toán có thể sử dụng chúng thường nhanh hơn điểm cổ điển theo khoảng cách điểm và tính toán so sánh. (ví dụ: đối với các hoạt động sử dụng DirectX, OpenGL, ...)

Vì vậy, tôi nghĩ rằng bạn nên bắt đầu ở đây:

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

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

... và như Beska đã nhận xét ở trên:

Điều này có thể không mang lại sự khác biệt "có thể nhìn thấy" tốt nhất ...

Điều đó cũng có nghĩa là thuật toán của bạn phụ thuộc vào định nghĩa "tương tự" của bạn nếu bạn đang xử lý hình ảnh.


1

Phiên bản Kotlin với bao nhiêu phần trăm bạn muốn khớp.

Phương thức gọi với phần trăm tùy chọn đối số

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

Phương pháp cơ thể

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

Bạn sẽ cần chuyển đổi bất kỳ màu RGB nào sang không gian màu Lab để có thể so sánh chúng theo cách con người nhìn thấy chúng. Nếu không, bạn sẽ có được màu RGB 'khớp' theo một số cách rất lạ.

Liên kết wikipedia về sự khác biệt màu sắc cung cấp cho bạn một phần giới thiệu về các thuật toán khác biệt không gian màu Lab khác nhau đã được xác định qua nhiều năm. Đơn giản nhất chỉ cần kiểm tra khoảng cách Euclidian của hai màu trong phòng thí nghiệm, hoạt động nhưng có một vài sai sót.

Thuận tiện có một triển khai Java của thuật toán CIEDE2000 tinh vi hơn trong dự án OpenIMAJ . Cung cấp cho nó hai bộ màu Lab của bạn và nó sẽ trả lại cho bạn giá trị khoảng cách duy nhất.


0

Cách "đúng" duy nhất để so sánh màu sắc là làm điều đó với deltaE trong CIELab hoặc CIELuv.

Nhưng đối với rất nhiều ứng dụng tôi nghĩ rằng đây là một xấp xỉ đủ tốt:

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

Tôi nghĩ rằng một khoảng cách manhattan có ý nghĩa hơn khi so sánh màu sắc. Hãy nhớ rằng màu gốc chỉ có trong đầu của chúng tôi. Họ không có bất kỳ ý nghĩa vật lý. CIELab và CIELuv được mô hình hóa theo thống kê từ nhận thức của chúng ta về màu sắc.


0

Để nhanh và bẩn, bạn có thể làm

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

sử dụng phân chia số nguyên để định lượng màu sắc.


0

Trả lời nhanh 5

Tôi tìm thấy chủ đề này bởi vì tôi cần một phiên bản Swift của câu hỏi này. Như chưa có ai trả lời với giải pháp, đây là của tôi:

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

Sử dụng:

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

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

Tôi đặt chênh lệch ít hơn 10% để trả về các màu tương tự, nhưng bạn có thể tự tùy chỉnh điều này.


0

API Android cho ColorUtils RGBToHSL: Tôi có hai màu int argb (color1, color2) và tôi muốn có khoảng cách / khác biệt giữa hai màu. Đây là những gì tôi đã làm;

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

Sau đó, tôi sử dụng mã dưới đây để tìm khoảng cách giữa hai màu.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.