Как сравнить два цвета для сходства/разницы

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

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

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

Ответ 1

См. статью Википедии о Разница цветов для правильных ведет. В принципе, вы хотите вычислить метрику расстояния в некотором многомерном цветовом пространстве. Но RGB не "воспринимается единообразно", поэтому ваш показатель расстояния Euclidean RGB, предложенный Вадимом, не будет соответствовать воспринимаемому человеком расстоянию между цветами. Для начала Lab * предназначен для восприятия однородного цветового пространства, и обычно используется метка deltaE. Но есть более совершенные цветовые пространства и более изысканные формулы deltaE, которые приближаются к соответствию восприятию человека.

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

Ответ 2

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

F.E.

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)

Ответ 3

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

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

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

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

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

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

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

Ответ 4

Если у вас есть два объекта 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.

Ответ 5

На самом деле я шел тем же путем пару месяцев назад. нет идеального ответа на вопрос (который был задан здесь пару раз), но есть еще один сложный, а затем ответ 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));
}

Ответ 6

Одним из лучших методов сравнения двух цветов с помощью человеческого восприятия является 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));
}
}

Ответ 7

Еще один ответ, хотя он похож на Supr one - просто другое цветовое пространство.

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

Это не проблема. Разница не такая уж большая, я думаю, но если вы хотите решить эту проблему "лучше", вы должны преобразовать цвета RGB в цветовое пространство, которое было специально разработано, чтобы избежать вышеуказанной проблемы. Есть несколько улучшений от более ранних моделей (поскольку это основано на человеческом восприятии, нам нужно измерить "правильные" значения на основе экспериментальных данных). Там Lab colorspace, которое, я думаю, было бы лучшим, хотя было бы немного сложно его преобразовать. Более простым будет CIE XYZ one.

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

Ответ 8

Лучший способ - deltaE. DeltaE - это число, которое показывает разницу цветов. Если дельта < 1 тогда разница не может распознаваться глазами человека. Я написал код в canvas и js для преобразования rgb в лабораторию, а затем вычисления delta e. В этом примере код распознает пиксели, которые имеют другой цвет с базовым цветом, который я сохранил как LAB1. а затем, если он отличается, эти пиксели краснеют. Вы можете увеличить или уменьшить чувствительность цветовой разницы с увеличением или уменьшить допустимый диапазон дельта. В этом примере я назначил 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>

Ответ 9

Все приведенные ниже методы приводят к шкале от 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;
    }
}

Ответ 10

Я использовал это в моем 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";

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

Ответ 11

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

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

Итак, я думаю, вы должны начать здесь:

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

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

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

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

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

Ответ 12

Простым методом, который использует только 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)

Я использовал это сейчас некоторое время, и он работает достаточно хорошо для большинства целей.

Ответ 13

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

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

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

Ответ 14

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

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

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

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

Ответ 15

Для быстрого и грязного вы можете сделать

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

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

Ответ 16

Я пробовал различные методы, такие как цветовое пространство 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

Ответ 17

Версия 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
}