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));
?>
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With