Как сравнить два цвета по сходству / разнице

171

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

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

Если вы можете сказать мне, какие шаги предпринять, даже если это вещи для меня, чтобы думать и делать вручную. Это было бы очень полезно.

Ана Фернандес
источник
1
Я сделал небольшое изменение в тексте, изменив португальское слово на то, что я считаю правильным английским эквивалентом ... измените его обратно, если я ошибся.
Беска
13
Существует статья в Википедии о различии цветов: en.wikipedia.org/wiki/Color_difference
Ocaso Protal
4
Это должно быть интересно: stevehanov.ca/blog/index.php?id=116 В нем рассматриваются вычисления различий в трех разных цветовых моделях.
Влад
Эй, @OcasoProtal, это отличная ссылка, спасибо, что поделились. А к ОП интересный вопрос.
Восприятие
Постарайтесь свести к минимуму любую потенциальную фотографическую изменчивость ... подробнее в ответе ниже.
Беска

Ответы:

130

Смотрите статью в Википедии о разнице в цвете, чтобы найти правильные выводы. По сути, вы хотите вычислить метрику расстояния в некотором многомерном цветовом пространстве. Но RGB не «восприимчиво однороден», поэтому ваша евклидова метрика расстояния RGB, предложенная Вадимом, не будет соответствовать воспринимаемому человеком расстоянию между цветами. Для начала предполагается, что L a b * является воспринимаемым однородным цветовым пространством, и обычно используется метрика deltaE. Но есть более изощренные цветовые пространства и более изощренные формулы deltaE, которые приближаются к человеческому восприятию.

Вам нужно больше узнать о цветовых пространствах и осветительных приборах, чтобы сделать преобразования. Но для быстрой формулы, которая лучше, чем евклидова метрика RGB, просто сделайте это: предположите, что ваши значения RGB находятся в цветовом пространстве sRGB, найдите формулы преобразования sRGB в L a b *, преобразуйте цвета sRGB в L a b *, и вычислите deltaE между вашими двумя значениями L a b *. Это не дорого в вычислительном отношении, это просто некоторые нелинейные формулы и некоторые умножения и сложения.

Людвикас Букис
источник
11
+1 для «deltaE», это самый стандартизированный метод сравнения, и есть адаптация формулы deltaE для разных вариантов использования.
Мартин Хеннингс
9
Вы можете найти формулы преобразования здесь: brucelindbloom.com/index.html?Equations.html
Гильермо Гутьеррес,
4
Или, если вы работаете в Ruby, посмотрите colorгем, который реализует deltaE среди других цветовых операций.
Майк Ярема,
Вот суть вышеупомянутой реализации в Javascript gist.github.com/ryancat/9972419b2a78f329ce3aebb7f1a09152
Ryan.C
46

Просто идея, которая впервые пришла мне в голову (извините, если глупо). Три компонента цвета могут быть приняты трехмерные координаты точек, а затем вы можете рассчитать расстояние между точками.

FE

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 будет значительно лучше цветовых пространств, чем (s) RGB для сопоставления сходства на основе расстояния.
Джон Пурди
4
Это хороший способ рассказать вам, насколько разные цвета существуют, но плохо показывает, насколько они различаются. Человеческие глаза далеки от совершенства: мы более чувствительны к зеленому, чем к красному или синему, наше восприятие яркости логарифмически и т. Д. ОП никогда не определял, какой он / она хочет; но посмотрите здесь для алгоритма, специально предназначенного для человеческого зрения.
BlueRaja - Дэнни Пфлюгофт
+ Это и моя первая идея.
ST3
9
Другая проблема здесь - 255, 0, 0 - это то же расстояние от 0, 255, 0, сколько и 0, 0, 255.
27

на самом деле я прошел тот же путь пару месяцев назад. не существует идеального ответа на вопрос (который задавался здесь пару раз), но есть еще один более изощренный ответ, чем sqrt (rr) и т. д., и его проще реализовать непосредственно в RGB, не переходя во все виды альтернативных цветовых пространств. Я нашел эту формулу здесь, которая является недорогой аппроксимацией довольно сложной реальной формулы (по CIE, которая является 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));
}
alonisser
источник
этот метод работал для меня. Это помогло мне найти ближайший цвет из списка названий цветов.
faisalbhagat
23

Значение цвета имеет более одного измерения, поэтому нет никакого способа сравнить два цвета. Вы должны определить для своего варианта использования значение цветов и, таким образом, как их лучше сравнить.

Скорее всего, вы хотите сравнить свойства оттенка, насыщенности и / или яркости цветов в отличие от красного / зеленого / синего компонентов. Если вам сложно понять, как вы хотите их сравнить, возьмите несколько пар образцов цветов и сопоставьте их мысленно, а затем попытайтесь объяснить / объяснить себе, почему они похожи / отличаются.

Как только вы знаете, какие свойства / компоненты цветов вы хотите сравнить, вам нужно выяснить, как извлечь эту информацию из цвета.

Скорее всего, вам просто нужно будет преобразовать цвет из общего представления RedGreenBlue в HueSaturationLightness, а затем вычислить что-то вроде

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

Этот пример даст вам простое скалярное значение, показывающее, насколько далеко градиент / оттенок цветов находятся друг от друга.

Смотрите HSL и HSV в Википедии .

Supr
источник
2
Из того, что я помню из моих лекций об этих вещах, я бы преобразовал изображение в цветовое пространство Lab, а не в HSV / HSL. Есть ли какие-либо основания для выбора этого?
Во
Нет. RGB и HSL - это те, с которыми я больше всего знаком, поэтому я выбрал HSL, чтобы подчеркнуть идею, что RGB по умолчанию - не единственный вариант - он действительно зависит от приложения. Спасибо, что сообщили мне о цветовом пространстве Lab.
Supr
1
Я все равно дал вам +1, потому что основным принципом здесь является «правильный» ответ (преобразуйте в цветовое пространство, которое обрабатывает воспринимаемую разницу равномерно, затем проведите сравнение). Я не уверен, какое пространство будет лучшим - все эти разные цветовые пространства запутаны, как черт возьми;)
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.

KBA
источник
Еще 2 второстепенные вещи: <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 в цветовое пространство, специально разработанное для избежания вышеуказанной проблемы. Есть несколько улучшений по сравнению с более ранними моделями (поскольку это основано на человеческом восприятии, мы должны измерить «правильные» значения на основе экспериментальных данных). Есть цветовое пространство Lab, которое, я думаю, будет лучшим, хотя и немного сложным для преобразования. Проще было бы CIE XYZ .

Вот сайт, на котором перечислены формулы для преобразования между различными цветовыми пространствами, чтобы вы могли немного поэкспериментировать.

Voo
источник
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;
    }
}
Vozzie
источник
2

Лучший способ - это deltaE. DeltaE - это число, показывающее разницу цветов. Если дельта <1, то разница не распознается человеческими глазами. Я написал код в canvas и js для преобразования rgb в lab и затем вычисления 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. Дельта Е означает изменение евклидова. То есть, в стандартном цветовом пространстве Lab, евклидово расстояние определяется вашей очень стандартной евклидовой формулой расстояния. Принимая во внимание, что модификации Delta E, а именно 76, 94, 2000 (есть также Delta E, CMC, которая используется для текстиля и тому подобное), представляют собой различные формулы расстояния между позициями в цветовом пространстве Lab. Код для лаборатории одинаков в каждом, код для разницы в цветах - нет. , Короче говоря, Delta E, это не то, что называется.
Tatarize
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";

Это работает достаточно хорошо.

SimpleCoder
источник
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

... и как уже говорила Беска выше:

Это может не дать лучшую "видимую" разницу ...

Это означает также, что ваш алгоритм зависит от вашего определения "аналогично", если вы обрабатываете изображения.

Beachwalker
источник
1

Версия Kotlin с каким процентом вы хотите соответствовать.

Вызов метода с необязательным аргументом процента

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 в цветовое пространство Lab, чтобы иметь возможность сравнивать их так, как их видят люди. В противном случае вы получите цвета RGB, которые «совпадают» очень странными способами.

Ссылка на Википедию о цветовых различиях дает вам представление о различных алгоритмах разницы в цветовом пространстве Lab, которые были определены на протяжении многих лет. Самое простое, которое просто проверяет евклидово расстояние двух лабораторных цветов, работает, но имеет несколько недостатков.

Удобно, что в проекте OpenIMAJ реализована Java-реализация более сложного алгоритма CIEDE2000 . Предоставьте ему два набора цветов Lab, и он вернет вам единичное значение расстояния.

AiTuDou
источник
0

Единственный «правильный» способ сравнения цветов - это делать с deltaE в CIELab или CIELuv.

Но для многих приложений я думаю, что это достаточно хорошее приближение:

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

Я думаю, что взвешенное расстояние в Манхэттене имеет гораздо больше смысла при сравнении цветов. Помните, что цветовые праймериз есть только у нас в голове. Они не имеют никакого физического значения. CIELab и CIELuv моделируются статистически исходя из нашего восприятия цвета.

onemasse
источник
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);
}

использование целочисленного деления для квантования цветов.

Austin_Anderson
источник
0

Swift 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

Android для ColorUtils API 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);
}
Капс
источник