16

I'm looking for a function that can accurately represent the distance between two colours as a number or something.

For example I am looking to have an array of HEX values or RGB arrays and I want to find the most similar colour in the array for a given colour

eg. I pass a function a RGB value and the 'closest' colour in the array is returned

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Phil
  • 665
  • 5
  • 9
  • 14
  • Similar question: http://stackoverflow.com/questions/1313/followup-finding-an-accurate-distance-between-colors – Kai Oct 27 '09 at 21:49

6 Answers6

22

Each color is represented as a tuple in the HEX code. To determine close matches you need to subtract each RGB component separately.

Example:

Color 1: #112233 
Color 2: #122334
Color 3: #000000

Difference between color1 and color2: R=1,  G=1   B=1  = 0x3 
Difference between color3 and color1: R=11, G=22, B=33 = 0x66

So color 1 and color 2 are closer than
1 and 3.

edit

So you want the closest named color? Create an array with the hex values of each color, iterate it and return the name. Something like this;

function getColor($rgb)
{
    // these are not the actual rgb values
    $colors = array(BLUE =>0xFFEEBB, RED => 0x103ABD, GREEN => 0x123456);

    $largestDiff = 0;
    $closestColor = "";
    foreach ($colors as $name => $rgbColor)
    {
        if (colorDiff($rgbColor,$rgb) > $largestDiff)
        {
            $largestDiff = colorDiff($rgbColor,$rgb);
            $closestColor = $name;
        }

    }
    return $closestColor;

}

function colorDiff($rgb1,$rgb2)
{
    // do the math on each tuple
    // could use bitwise operates more efficiently but just do strings for now.
    $red1   = hexdec(substr($rgb1,0,2));
    $green1 = hexdec(substr($rgb1,2,2));
    $blue1  = hexdec(substr($rgb1,4,2));

    $red2   = hexdec(substr($rgb2,0,2));
    $green2 = hexdec(substr($rgb2,2,2));
    $blue2  = hexdec(substr($rgb2,4,2));

    return abs($red1 - $red2) + abs($green1 - $green2) + abs($blue1 - $blue2) ;

}
Deproblemify
  • 3,340
  • 5
  • 26
  • 41
Byron Whitlock
  • 52,691
  • 28
  • 123
  • 168
  • I understand I need to get the RGB values, but im looking for a function that for can return the "most similar" color in an array for a given color. A simple example: [BLUE , RED, DARKRED, GREEN] If the function was given the color LIGHTBLUE, from the array above, Id expect the function to return "BLUE" Cheers Philip – Phil Oct 27 '09 at 21:52
  • If you aren't suing hex values at all, just create a function with a big switch statement that takes the color name and just returns what you think is the closest color name. – Byron Whitlock Oct 27 '09 at 22:19
  • 3
    The colofDiff function is good, but has to fix a bug in return, "return abs($red1 - $red2) + abs($green1 - $green2) + abs($blue1 - $blue2) ;" – Cesar Oct 27 '09 at 22:30
  • 1
    I know this post is years old... but I noticed that for this too work two things have to be changed: 1. we are looking for the smallestDiff (inorder to gt the closest color, right?) so change the `colorDiff($rgbColor,$rgb) > $largestDiff` to `<` and 2. we should save the numbers as strings instead of HEX-Numbers because we are using `substr()`. – Deproblemify Jun 28 '15 at 10:57
  • This post is even more years old now but you are right @Deproblemify and further issues. As you say: 1) `colorDiff($rgbColor,$rgb) > $largestDiff)` needs to be `colorDiff($rgbColor,$rgb) < $largestDiff)` but also 2) initial value of $largestDiff should be set as 256 not 0 so that each color can be compared to see if it is closer than max range away – Gideon Nov 09 '21 at 21:50
14

Here is a paper on the subject which should give a good answer.

I was thinking that converting to HSL/HSV first would be a good idea also, but then I realized that at extreme values of S & L/V, H doesn't matter, and in the middle, it matters most.

I think if you want a simple solution, staying in the RGB space would be wiser. I'd use cartesian distance. If you're considering color R G B against Ri Gi Bi for several i, you want the i that minimizes

(R - Ri)^2 + (G - Gi)^2 + (B - Bi)^2
Grumdrig
  • 16,588
  • 14
  • 58
  • 69
10

First you have to choose th appropriate color space you want the color comparisons to occur in (RGB, HSV, HSL, CMYK, etc.).

Assuming you want to know how close two points in the 3-dimenionsal RGB space are to each other, you can calculate the Pythagorean distance between them, i.e.,

d2 = (r1 - r2)**2 + (g1 - g2)**2 + (b1 - b2)**2;

This actually gives you the square of the distance. (Taking the square root is not necessary if you're comparing only the squared values.)

This assumes that you want to treat the R, G, and B values equally. If you'd rather weight the individual color components, such as what happens when you convert RGB into grayscale, you have to add a coefficient to each term of the distance, i.e.,

d2 = 30*(r1-r2)**2 + 59*(g1-g2)**2 + 11*(b1-b2)**2;

This assumes the popular conversion from RGB to grayscale of 30% red + 59% green + 11% blue.

Update

That last equation should probably be

d2 = (30*(r1-r2))**2 + (59*(g1-g2))**2 + (11*(b1-b2))**2;
David R Tribble
  • 11,918
  • 5
  • 42
  • 52
4

A very simple approach is to calculate the summarized distance among the three dimensions. For example simple_distance("12,10,255","10,10,250")=7

A more sophisticated approach would be to take the square of the distances for each components and sum those - this way components being too far would be "punished" more: square_distance("12,10,255","10,10,250")=2*2+0*0+5*5=29.

Of course you would have to iterate over the list of colors and find the closest one.

Alexey
  • 5,898
  • 9
  • 44
  • 81
Gergely Orosz
  • 6,475
  • 4
  • 47
  • 59
2

you can convert your RGB value to HSL or HSV. then the colors are easy to compare: order colors by hue first, then by saturation, then by luminance. in the resulting order, 2 colors next to each other will appear as being very close perceptually.

beware that hue wraps around: for a hue ranging from 0 to 255, a hue of 0 and a hue of 255 are very close.

see the wikipedia article on HSL http://en.wikipedia.org/wiki/HSL_and_HSV for the formula which will allow you to convert RGB to HSL

(note that other color spaces, like L.a.b., may give better results, but conversion is more complicated)

let's define this mathematically:

distance(A(h,s,l), B(h,s,l)) = (A(h)-B(h)) * F^2 + (A(s)-B(s)) * F + (A(l)-B(l))

where F is a factor carefully chosen (something like 256...)

the above formula does not take into account the hue wraparound...

Adrien Plisson
  • 22,486
  • 6
  • 42
  • 73
  • this makes no sense. If colors A and B had a slight difference in luminance and same hue, and colors A and C had the same luminance and a big difference in hue (say one is red and the other is green...), the distance between A and B would be greater than the distance between A and C... – Walter Tross Jun 07 '12 at 16:30
  • it seems you misunderstood the ordering i propose: you first order by hue, then you order colors with equal hue by saturation, finally you order colors with equal hue and saturation by luminance. given this ordering, colors A and B have the same hue and the distance (A,B) is smaller than (A,C). – Adrien Plisson Jun 07 '12 at 18:53
  • i edited my answer to include a small mathematical formula, which, i hope, helps understanding the ordering better... – Adrien Plisson Jun 07 '12 at 19:01
  • this does not make any sense either, sorry. Just take my first comment, swap the words "luminance" and "hue", and remove the part in parentheses. – Walter Tross Jun 08 '12 at 12:08
1

Color perception is not linear because the human eye is more sensitive to certain colors than others.

You need to use a special formula.

Look here.

Community
  • 1
  • 1
Pylyp
  • 404
  • 1
  • 7
  • 15