Як порівняти два кольори для подібності / різниці


171

Я хочу розробити програму, яка допоможе мені оцінити між 5 заздалегідь визначеними кольорами, який із них більше схожий на змінний колір та з яким відсотком. Вся справа в тому, що я не знаю, як це зробити вручну крок за кроком. Тому навіть складніше думати про програму.

Більш детально: кольори представлені на фотографіях пробірки з гелем, які мають різні кольори. У мене 5 трубочок з різними кольорами, кожна з них є представницею 1 з 5 рівнів. Я хочу сфотографувати інші зразки і на комп’ютері оцінити, до якого рівня належить цей зразок, порівнюючи кольори, і я хочу знати, що з відсотком наближення теж. Я хотів би програму, яка робить щось подібне: http://www.colortools.net/color_matcher.html

Якщо ви можете сказати мені, які кроки потрібно зробити, навіть якщо це речі, я думаю, і робити вручну. Було б дуже корисно.


1
Я змінив текст, змінивши португальське слово на те, що, на мою думку, є правильним англійським еквівалентом ... змінити його назад, якщо помилився.
Беська

13
Є стаття у Вікіпедії про різницю кольорів: en.wikipedia.org/wiki/Color_difference
Окасо Проталь

4
Це має бути цікаво: stevehanov.ca/blog/index.php?id=116 Він досліджує обчислення різниці трьох різних кольорових моделей.
Влад

Привіт @OcasoProtal, це чудове посилання, дякую за обмін. А до ОП цікаве питання.
Сприйняття

Спробуйте також мінімізувати будь-яку потенційну фотографічну мінливість ... докладніше у відповіді нижче.
Беска

Відповіді:


130

Дивіться статтю Вікіпедії про різницю кольорів для правильних відводів В основному ви хочете обчислити метрику відстані в деякому багатовимірному просторі кольорів. Але RGB не є "перцептивно сприйнятливим", тому ваша евклідова RGB-метрика відстані, запропонована Вадимом, не буде відповідати людській відстані між кольорами. Для початку L a b * призначений для сприйняття рівномірного кольорового простору, і зазвичай використовується показник deltaE. Але є більш вишукані кольорові простори та більш вишукані формули deltaE, які наближаються до відповідності людському сприйняттю.

Вам доведеться дізнатися більше про простори кольорів та освітлювачі, щоб зробити перетворення. Але для швидкої формули , яка краще , ніж евклидова метрика RGB, просто зробити це: припустимо , що ваші значення RGB в колірному SRGB, знайти SRGB до L б * перетворення формули, конвертувати SRGB кольору L б *, і обчислити deltaE між вашими двома значеннями L a b *. Це не обчислювально дорого, це лише деякі нелінійні формули та деякі множення та додавання.


11
+1 для "deltaE", це найбільш стандартизований метод порівняння і є адаптації формули deltaE для різних випадків використання.
Мартін Хеннінгс

9
Ви можете знайти формули перетворення тут: brucelindbloom.com/index.html?Equations.html
Guillermo Gutiérrez

4
Або якщо ви працюєте в Ruby, перегляньте colorдорогоцінний камінь, який реалізує deltaE серед інших кольорових операцій.
Майк Джарема

Ось суть вищезгаданої реалізації в Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C

46

Просто ідея, яка мені вперше прийшла в голову (вибачте, якщо дурна). За трьома компонентами кольорів можна припустити тривимірні координати точок, а потім можна обчислити відстань між точками.

ЗП

Point1 has R1 G1 B1
Point2 has R2 G2 B2

Відстань між кольорами -

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

Відсоток

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

28
Якщо ми використовуємо кольоровий простір RGB, різниця між двома кольорами не є такою ж, як люди сприймають різницю. Але так, основна ідея скрізь однакова - нам просто доведеться відобразити її в інший кольоровий простір (лабораторія, я думаю)
Voo

6
@Voo: Я згоден, HSV / HSL / LAB буде значно кращим кольоровим простором, ніж RGB (RGB) для відповідності подібності на відстані.
Джон Перді,

4
Це хороший спосіб розповісти про те, наскільки різні кольори є, але це погана робота, щоб розповісти, наскільки вони будуть досконалі. Людські очі далеко не ідеальні: ми чутливіші до зеленого, ніж до червоного чи блакитного, наше сприйняття яскравості - лігритмічне і т. Д. ОП ніколи не вказував, кого він хоче; але дивіться тут алгоритм, спеціально підібраний для зору людини.
BlueRaja - Danny Pflughoeft

+ Це також моя перша ідея.
ST3

9
Ще одна проблема тут - 255, 0, 0 - така ж відстань від 0, 255, 0, як і 0, 0, 255.

27

насправді я пішов тим самим шляхом пару місяців тому. не існує ідеальної відповіді на питання (про що тут задавали пару разів), але є ще один складний, ніж відповідь sqrt (rr) і т. д. і простіший в реалізації безпосередньо з RGB, не переходячи до всіляких альтернативних кольорових просторів. Я знайшов цю формулу тут , яка є низькою вартістю наближення досить ускладненою реальної формули (МКО , яка є W3C кольором, так як це не закінчений квест, ви можете знайти старе і просте цветоразностних там рівняння). Щасти

Редагувати: Для нащадків, ось відповідний код C:

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

цей метод спрацював на мене. Це допомогло мені знайти найближчий колір із списку кольорових назв.
faisalbhagat

23

Значення кольору має більше, ніж один вимір, тому не існує властивого способу порівняння двох кольорів. Ви повинні визначити для ваших випадків використання значення кольорів і тим самим як їх найкраще порівняти.

Швидше за все, ви хочете порівняти властивості кольорів, насиченості та / або світлості кольорів у порівнянні з компонентами червоного / зеленого / синього. Якщо у вас виникають труднощі з'ясувати, як ви хочете їх порівняти, візьміть кілька пар зразкових кольорів і подумки порівняйте їх, то спробуйте виправдати / пояснити собі, чому вони схожі / різні.

Коли ви дізнаєтеся, які властивості / компоненти кольорів ви хочете порівняти, то вам потрібно з’ясувати, як витягти цю інформацію з кольору.

Швидше за все, вам просто потрібно буде перетворити колір із загального представлення RedGreenBlue в HueSaturationLightness, а потім обчислити щось на зразок

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

Цей приклад дасть вам просте скалярне значення, яке вказує, наскільки градієнт / відтінок кольорів знаходяться один від одного.

Дивіться HSL та HSV у Вікіпедії .


2
З речей, які я пам'ятаю з своїх лекцій про ці речі, я б перетворив зображення в кольоровий простір лабораторії, а не HSV / HSL. Будь-які міркування для вибору цього?
Во

Ні. RGB і HSL - це ті, з ким я найбільше знайомий, тому я вибрав HSL лише для того, щоб підкреслити думку про те, що RGB "за замовчуванням" - не єдиний варіант - він дійсно залежить від програми. Дякуємо, що повідомили про кольоровий простір лабораторії.
Супр

1
Я дав вам +1 так чи інакше, тому що основний принцип тут - "правильна" відповідь (перетворіть у кольоровому просторі, який обробляє сприйняту різницю рівномірно, а потім порівняйте). Я не впевнений, який простір був би найкращим - усі ці різні кольорові простори плутають, як пекло imo;)
Voo

21

Якщо у вас є два Colorоб'єктів c1і c2ви можете просто порівняти кожне значення RGB з 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());

Ці значення ви можете просто розділити на величину різницевих насичень (255), і ви отримаєте різницю між двома.

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

Після чого можна просто знайти середню різницю кольорів у відсотках.

(pctDiffRed + pctDiffGreen + pctDiffBlue) / 3 * 100

Що б дало вам різницю у відсотках між c1та c2.


Ще дві незначні речі: <b> 1 </b> pctDiffRed = diffRed / 255;дасть вам 0, якщо ви не кинетесь десь на поплавок. <b> 2 </b> Вам потрібно десь помножити на 100, щоб отримати відсоток.
vaughandroid

18
Це може не дати найкращої "видимої" різниці, оскільки людське око сприймає зміни кольору по-різному. Зважаючи на це, я здогадуюсь, це саме те, що вона шукає, тому що вона, ймовірно, шукає однаково кількісно відмінність, а не сприйняту різницю. Просто подумав, що я б це тут розглядав як щось, щоб розглянути, якщо це актуально.
Беска

14

Один з найкращих методів порівняння двох кольорів за сприйняттям людини - це CIE76. Різниця називається Delta-E. Коли вона менше 1, людське око не може розпізнати різницю.

Існує чудовий утиліти класу ColorUtils (код нижче), який включає методи порівняння CIE76. Він написаний Даніелем Стребелем, Цюріхський університет.

З ColorUtils.class я використовую метод:

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

r1, g1, b1 - значення RGB першого кольору

r2, g2, b2 - значення RGB від другого кольору, який ви хочете порівняти

Якщо ви працюєте з Android, ви можете отримати такі значення, як це:

r1 = Color.red(pixel);

g1 = Color.green(pixel);

b1 = Color.blue(pixel);


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

наведений вище код має помилку в rgb2lab: ділення на 12 слід замінити діленням на 12,92 в перерахунку r, g і b. інакше функція не є неперервною при r = 0,04045
Джон Сміт

10

Ще одна відповідь, хоча вона схожа на ту, яку супр - просто інший кольоровий простір.

Річ у тому, що люди сприймають різницю в кольорі не рівномірно, і кольоровий простір RGB це ігнорує. Як результат, якщо ви використовуєте кольоровий простір RGB і просто обчислюєте евклідову відстань між двома кольорами, ви можете отримати різницю, яка є математично абсолютно правильною, але не збігається з тим, що вам скажуть люди.

Це може не бути проблемою - різниця не така велика, я думаю, але якщо ви хочете вирішити це "краще", вам слід перетворити свої кольори RGB в кольоровий простір, який був спеціально розроблений, щоб уникнути вищевказаної проблеми. Є декілька, вдосконалення від попередніх моделей (оскільки це засновано на сприйнятті людини, нам потрібно вимірювати "правильні" значення на основі експериментальних даних). Існує кольоровий простір лабораторії, який, я думаю, було б найкращим, хоча трохи складним для його перетворення. Простішим буде CIE XYZ .

Ось сайт, у якому перераховано формули для перетворення між різними кольоровими просторами, щоб ви могли трохи експериментувати.


3

Усі наведені нижче методи призводять до шкали від 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

Найкращий спосіб - дельтаЕ. DeltaE - це число, яке показує різницю кольорів. Якщо дельта <1, то різницю людськими очима не впізнати. Я написав код у полотні та js для перетворення rgb в лабораторію, а потім обчислення delta e. У цьому прикладі код розпізнає пікселі, які мають різний колір із базовим кольором, який я зберег як LAB1. і тоді, якщо це інше, ці пікселі стають червоними. Ви можете збільшити або зменшити чутливість різниці кольорів із збільшенням або зменшенням допустимого діапазону дельти e. У цьому прикладі я призначив 10 для deltaE у рядку, який я написав (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
Мене трохи турбують деякі ваші цілі поділи. 1/3і 16/116обидва оцінюйте 0, що майже точно не те, що ви хочете. Можливо, ваш алгоритм правильний, але ваш код точно не є.
Давуд ібн Карім

Ви описуєте CIE-LAB dE94. Дельта Е означає зміну евклідової. Що можна сказати в стандартному кольоровому просторі лабораторії, евклідовій відстані, заданій вашою дуже стандартною евклідовою формулою відстані. Тоді як модифікації Delta E, а саме 76, 94, 2000 (також є Delta E, CMC, який використовується для текстилю тощо), є різними формулами відстані між положеннями в кольоровому просторі лабораторії. Код лабораторії однаковий у кожного, код різниці кольорів - ні. . Словом, Delta E - це не те, що називається.
Татариз

2

Простий метод, який використовує лише RGB - це

cR=R1-R2 
cG=G1-G2 
cB=B1-B2 
uR=R1+R2 
distance=cR*cR*(2+uR/256) + cG*cG*4 + cB*cB*(2+(255-uR)/256)

Я використовую цей деякий час зараз, і він працює досить добре для більшості цілей.


Використовуючи вищезгадану формулу, який діапазон значень відстані
Аман Агарвал

це досить близько до евклідової наближення різниці кольорів. Я здогадуюсь, що він пропускає кореневий компонент для прискорення обчислення, тому це діапазон від 0 до 100 ^ 3. Якщо ви хочете нормалізуватися до 100, зробіть відстань до влади1/3
Даніель,

2

Я використовував це в моєму Android, і це здається задовільним, хоча 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);
}

Тоді я використав наступне, щоб отримати відсоток схожості:

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

Це працює досить добре.


2

Я спробував різні методи, такі як кольоровий простір LAB, порівняння HSV, і я виявив, що для цієї мети світність справляється досить добре.

Ось версія 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))

Дам тобі

0.0687619047619048

Яке походження ImageColor? редагувати Я знайшов, цеfrom PIL import ImageColor
ademar111190

Чи не яскравість яскравість кольору? Так що в цьому випадку зелений, синій та червоний колір не будуть повідомлятися різними, доки яскравість однакова?
Петро Б.

1

Я думаю, ви хочете проаналізувати ціле зображення наприкінці, чи не так? Таким чином, ви могли перевірити наявність найменшої / найвищої різниці до кольорової матриці ідентичності.

Більшість математичних операцій для обробки графіки використовують матриці, тому що можливі алгоритми їх використання часто швидші, ніж класичні розрахунки за точковим відстанню та порівняння. (наприклад, для операцій, що використовують DirectX, OpenGL, ...)

Тому я думаю, що ви повинні почати тут:

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

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

... і як Беска вже коментував вище:

Це може не дати найкращої "видимої" різниці ...

Що означає також, що ваш алгоритм залежить від вашого визначення "подібного до", якщо ви обробляєте зображення.


1

Версія Котліна, на скільки відсотків ви хочете відповідати.

Виклик методу з відсотковим аргументом

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

Метод органу

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

Вам потрібно буде перетворити будь-які кольори RGB в кольоровий простір Лабораторії, щоб мати можливість порівнювати їх таким чином, як їх бачать люди. Інакше ви отримаєте RGB кольори, які 'відповідають' якимись дуже дивними способами.

Посилання вікіпедії про кольорові відмінності дає вам змогу ознайомитись з різними алгоритмами розбіжності кольорів у Лабораторії, які були визначені роками. Найпростіший, який просто перевіряє евклідову відстань двох лабораторних кольорів, працює, але має кілька недоліків.

У проекті OpenIMAJ зручно є реалізація Java більш складного алгоритму CIEDE2000 . Надайте йому два набори кольорів лабораторії, і це поверне вам значення одного відстані.


0

Єдиний "правильний" спосіб порівняння кольорів - це зробити це з deltaE в CIELab або CIELuv.

Але для багатьох застосувань я думаю, що це досить хороше наближення:

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

Я думаю, що зважена відстань на Манхеттені має набагато більше сенсу при порівнянні кольорів. Пам’ятайте, що кольорові праймери лише в нашій голові. Вони не мають жодного фізичного значення. CIELab та CIELuv моделюється статистично з нашого сприйняття кольору.


0

Для швидкого і брудного ви можете зробити

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

використовуючи ціле ділення для квантування кольорів.


0

Швидкий 5 відповідь

Я знайшов цю тему, тому що мені була потрібна версія Swift цього питання. Як ніхто не відповів на рішення, ось моє:

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

Використання:

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

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

Я встановив менше 10% різниці, щоб повернути подібні кольори, але ви можете налаштувати це самостійно.


0

API для Android для ColorUtils RGBToHSL: у мене було два кольори int argb (color1, color2), і я хотів отримати відстань / різницю між двома кольорами. Ось що я зробив;

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

Потім я використав код нижче, щоб знайти відстань між двома кольорами.

private float getDistance(getHue(color1), getHue(color2)) {
    float avgHue = (hue1 + hue2)/2;
    return Math.abs(hue1 - avgHue);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.