views:

136

answers:

1

I don't suppose anyone knows of a function (PHP, preferably) that can take a hex color code and give an approximate color name for that hex value. I don't need a solution with 100s of colors. Even if it just amounted to the colors white, black, red, green blue, brown orange and yellow, I'd be pretty well in shape.

If you don't know of an existing resource, does anyone know of a good way to approach this problem?

Thanks in advance for the help.

+2  A: 

You have a list of colors here in wikipedia.

A naive implementation would just calculate the distance between the color whose name you want to retrieve and every one of those and output the one that's closer.

If you see the colors as vectors in R3, you could calculate the distance as L1 norm:

function distancel1(array $color1, array $color2) {
    return abs($color1[0] - $color2[0]) + 
        abs($color1[1] - $color2[1]) +
        abs($color1[2] - $color2[2]);
}

or the L2 norm:

function distancel2(array $color1, array $color2) {
    return sqrt(pow($color1[0] - $color2[0], 2) +
        pow($color1[1] - $color2[1], 2) +
        pow($color1[2] - $color2[2], 2));
}

For converting the hexadecimal notation, see here.

Example script:

<?php

$colors = array(
    "black"     => array(0, 0, 0),
    "green"     => array(0, 128, 0),
    "silver"    => array(192, 192, 192),
    "lime"      => array(0, 255, 0),
    "gray"      => array(128, 0, 128),
    "olive"     => array(128, 128, 0),
    "white"     => array(255, 255, 255),
    "yellow"    => array(255, 255, 0),
    "maroon"    => array(128, 0, 0),
    "navy"      => array(0, 0, 128),
    "red"       => array(255, 0, 0),
    "blue"      => array(0, 0, 255),
    "purple"    => array(128, 0, 128),
    "teal"      => array(0, 128, 128),
    "fuchsia"   => array(255, 0, 255),
    "aqua"      => array(0, 255, 255),
);

$value = "#819001";

function html2rgb($color)
{
    if ($color[0] == '#')
        $color = substr($color, 1);

    if (strlen($color) == 6)
        list($r, $g, $b) = array($color[0].$color[1],
                                 $color[2].$color[3],
                                 $color[4].$color[5]);
    elseif (strlen($color) == 3)
        list($r, $g, $b) = array($color[0].$color[0],
            $color[1].$color[1], $color[2].$color[2]);
    else
        return false;

    $r = hexdec($r); $g = hexdec($g); $b = hexdec($b);

    return array($r, $g, $b);
}

function distancel2(array $color1, array $color2) {
    return sqrt(pow($color1[0] - $color2[0], 2) +
        pow($color1[1] - $color2[1], 2) +
        pow($color1[2] - $color2[2], 2));
}

$distances = array();
$val = html2rgb($value);
foreach ($colors as $name => $c) {
    $distances[$name] = distancel2($c, $val);
}

$mincolor = "";
$minval = pow(2, 30); /*big value*/
foreach ($distances as $k => $v) {
    if ($v < $minval) {
        $minval = $v;
        $mincolor = $k;
    }
}

echo "Closest color: $mincolor\n";
Artefacto
Very, very slick. I'll have to try to implement it, and there may be tweaking involved, but otherwise, you've given me a solid foundation with which to work. Thanks much!
dclowd9901