Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically determine human readable color (e.g. Red, Green, etc..) of an image

Tags:

php

colors

I'm trying to come up with a script that will programmatically run through an image and tell me it's primary color(s).

Currently the script gets the RGB value of each pixel. Compares them against predefined rules and attempts to count up the number of pixels of each colour.

My problem is the script is a little hit and miss. Does anyone know of a better way of doing this (maybe using a different colour coding system that's easier to translate to english) or an existing set of rules defining colours via their RGB?

<?php
$file = "8629.jpg";

$colors = array("Red" => array("rel" => true, "r" => 0.65, "g" => 0.09, "b" => 0.25, "var" => 0.3),
                "Blue" => array("rel" => true, "r" => 0.21, "g" => 0.32, "b" => 0.46, "var" => 0.3),
                "Green" => array("rel" => true, "r" => 0, "g" => 0.67,"b" =>  0.33, "var" => 0.3),
                "Black" => array("rel" => false, "r" => 0, "g" => 0,"b" =>  0, "var" => 30),
                "White" => array("rel" => false, "r" => 255, "g" => 255,"b" =>  255, "var" => 30));                 

$total = 0;

$im = imagecreatefromjpeg($file);
$size = getimagesize($file);

if (!$im) {
    exit("No image found.");
}

for ($x = 1; $x <= $size[0]; $x++) {
    for($y = 1; $y <= $size[1]; $y++) {
        $rgb = imagecolorat($im, $x, $y);
        $r = ($rgb >> 16) & 0xFF;
        $g = ($rgb >> 8) & 0xFF;
        $b = $rgb & 0xFF;

        $colorTotal = $r + $g + $b;

        $rRatio = $r > 0 ? $r / $colorTotal : 0;
        $gRatio = $g > 0 ? $g / $colorTotal : 0;
        $bRatio = $b > 0 ? $b / $colorTotal : 0;

        foreach($colors as $key => $color) {
            if ($color["rel"]) {
                if ((($color["r"] - $color["var"]) <= $rRatio && $rRatio <= ($color["r"] + $color["var"])) &&
                    (($color["g"] - $color["var"]) <= $gRatio && $gRatio <= ($color["g"] + $color["var"])) &&
                    (($color["b"] - $color["var"]) <= $bRatio && $bRatio <= ($color["b"] + $color["var"]))) {

                    $colourCount[$key]++;
                    $total++;
                }
            } else {
                if ((($color["r"] - $color["var"]) <= $r && $r <= ($color["r"] + $color["var"])) &&
                    (($color["g"] - $color["var"]) <= $g && $g <= ($color["g"] + $color["var"])) &&
                    (($color["b"] - $color["var"]) <= $b && $b <= ($color["b"] + $color["var"]))) {

                    $colourCount[$key]++;
                    $total++;
                }
            }
        }
    }
}

var_dump($colourCount);

foreach($colourCount as $key => $color) {
    $colourPrecent[$key] = $color / $total;
}

arsort($colourPrecent);
var_dump($colourPrecent);

foreach($colourPrecent as $key => $color) {
    if ($prevVal) {
        if ($color < ($prevVal - 0.1)) {
            break;
        }
    }

    $primary[] = $key;
    $prevVal = $color;
}

echo("The primary colours in this image are " . implode(" and ", $primary));

?>
like image 494
Dan Avatar asked Sep 21 '11 14:09

Dan


1 Answers

Solution was to convert the RGB to HSL as suggested by Herbert. Function for converting to human still needs a little tweaking / finishing off but here it is:

function hslToHuman($h, $s, $l) {

$colors = array();

// Gray
if ($s <= 10 && (9 <= $l && $l <= 90)) {
    $colors[] = "gray";
}

$l_var = $s / 16;

// White
$white_limit = 93;
if (($white_limit + $l_var) <= $l && $l <= 100) {
    $colors[] = "white";
}

// Black
$black_limit = 9;
if (0 <= $l && $l <= ($black_limit - $l_var)) {
    $colors[] = "black";
}

// If we have colorless colors stop here
if (sizeof($colors) > 0) {
    return $colors;
}

// Red
if (($h <= 8 || $h >= 346)) {
    $colors[] = "red";
}

// Orange && Brown
// TODO

// Yellow
if (40 <= $h && $h <= 65) {
    $colors[] = "yellow";
}

// Green
if (65 <= $h && $h <= 170) {
    $colors[] = "green";
}

// Blue
if (165 <= $h && $h <= 260) {
    $colors[] = "blue";
}

// Pink && Purple
// TODO

return $colors;
}
like image 98
Dan Avatar answered Sep 20 '22 11:09

Dan