Calculate colordifference - php

Is it possible to check if a certain hexadecimal color is closer to FFF or 000 based on a defined 'center-value'?
I want to check if a color lies closer to #FFF or #000 based on #888.
So if I check for #EFEFEF it should return #FFF and if I try #878787 it should return #000.
How can this be achieved? I'm not sure what to search for on Google...
Thanks in advance

You could convert the colours to numbers:
$color_num = hexdec(substr($color, 1)); // skip the initial #
Then compare them to either 0x0 or 0xffffff.
You could also break them down into R, G and B and make three comparisons; then average them? Not sure how precise you want this thing :)

The easiest way to solve your problem is to calculate the distance between colors using their greyscale values (there are other ways, but this is simple). So something like:
// returns a distance between two colors by comparing each component
// using average of the RGB components, eg. a grayscale value
function color_distance($a, $b)
{
$decA = hexdec(substr($a, 1));
$decB = hexdec(substr($a, 1));
$avgA = (($decA & 0xFF) + (($decA >> 8) & 0xFF) + (($decA >> 16) & 0xFF)) / 3;
$avgB = (($decB & 0xFF) + (($decB >> 8) & 0xFF) + (($decB >> 16) & 0xFF)) / 3;
return abs($avgA - $avgB);
}
// I am going to leave the naming of the function to you ;)
// How this works is that it'll return $minColor if $color is closer to $refColorMin
// and $maxColor if $color is closer to $refColorMax
// all colors should be passed in format #RRGGBB
function foo($color, $refColorMin, $refColorMax, $minColor, $maxColor)
{
$distMin = color_distance($color, $refColorMin);
$distMax = color_distance($color, $refColorMax);
return ($distMin < $distMax) ? $minColor : $maxColor;
}
// Example usage to answer your original question:
$colorA = foo('#EFEFEF', '#888888', '#FFFFFF', '#000000', '#FFFFFF');
$colorA = foo('#898989', '#888888', '#FFFFFF', '#000000', '#FFFFFF');
// Check the values
var_dump($colorA, $colorB);
The output is:
string(7) "#FFFFFF"
string(7) "#000000"

You could do something like the following:
function hex2rgb($hex) {
$hex = str_replace("#", "", $hex);
if(strlen($hex) == 3) {
$r = hexdec(substr($hex,0,1).substr($hex,0,1));
$g = hexdec(substr($hex,1,1).substr($hex,1,1));
$b = hexdec(substr($hex,2,1).substr($hex,2,1));
} else {
$r = hexdec(substr($hex,0,2));
$g = hexdec(substr($hex,2,2));
$b = hexdec(substr($hex,4,2));
}
$rgb = array($r, $g, $b);
//return implode(",", $rgb); // returns the rgb values separated by commas
return $rgb; // returns an array with the rgb values
}
$rgb = hex2rgb("#cc0");
From that you could take the values of $rgb and see if their values, on average, area greater than or less than 122.5. If its greater than 122.5 you'd be closer to #FFFFFF, lower than 122.5 you'd be closer to #000000.

Thanks everybody for the help!
In my startpost I made a little typo as mentioned by oezi. For me the solution was a small modification to the accepted answer (by reko_t):
function color_distance($sColor1, $sColor2)
{
$decA = hexdec(substr($sColor1, 1));
$decB = hexdec(substr($sColor2, 1));
$avgA = (($decA & 0xFF) + (($decA >> 8) & 0xFF) + (($decA >> 16) & 0xFF)) / 3;
$avgB = (($decB & 0xFF) + (($decB >> 8) & 0xFF) + (($decB >> 16) & 0xFF)) / 3;
return abs($avgA - $avgB);
}
function determine_color($sInputColor, $sRefColor, $sMinColor, $sMaxColor)
{
$distRef = color_distance($sInputColor, $sRefColor);
$distMin = color_distance($sInputColor, $sMinColor);
$distMax = color_distance($sInputColor, $sMaxColor);
return $distMax - $distRef < $distMin - $distRef ? $sMinColor : $sMaxColor;
}
I needed this function to determine text-color on a background-color which can be set by some setting. Thanks! :) Credits to reko_t!

Related

Change “Saturation” of an image with PHP GD Library?

There is an excellent answer on how to change the HUE of an image using PHP-GD library. But I need to know how to change the SATURATION of an image using PHP-GD. Here is a copy of the code from the answer which successfully changes the HUE of the image.
function imagehue(&$image, $angle) {
if($angle % 360 == 0) return;
$width = imagesx($image);
$height = imagesy($image);
for($x = 0; $x < $width; $x++) {
for($y = 0; $y < $height; $y++) {
$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$alpha = ($rgb & 0x7F000000) >> 24;
list($h, $s, $l) = rgb2hsl($r, $g, $b);
$h += $angle / 360;
if($h > 1) $h--;
list($r, $g, $b) = hsl2rgb($h, $s, $l);
imagesetpixel($image, $x, $y, imagecolorallocatealpha($image, $r, $g, $b, $alpha));
}
}
}
If you need to take a look at the code of helper functions rgb2hsl and hsl2rgb please check the original answer. Since Hue is one of the parameters of HSL, I thought I could modify the function somehow to get a working solution for Saturation. Despite of limited php skills I had to try but it did not work and produced bizarre results. Here is the modification that I am trying.
MODIFIED CODE: UPDATED as suggested by #mark
function imageSaturation(&$image, $saturationPercentage) {
$width = imagesx($image);
$height = imagesy($image);
for($x = 0; $x < $width; $x++) {
for($y = 0; $y < $height; $y++) {
$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$alpha = ($rgb & 0x7F000000) >> 24;
list($h, $s, $l) = rgb2hsl($r, $g, $b);
$s = $s * (100 + $saturationPercentage ) /100;
if($s > 1) $s = 1;
list($r, $g, $b) = hsl2rgb($h, $s, $l);
imagesetpixel($image, $x, $y, imagecolorallocatealpha($image, $r, $g, $b, $alpha));
}
}
}
header('Content-type: image/png');
$image = imagecreatefrompng('rgb.png');
imageSaturation($image, -80);//bring down current image saturation to 80%
imagepng($image);
EFFORTS UPDATE :
It has been pointed out to me by #Dai that these lines help construct a color code of an individual RGB pixel. So I guess this part can remained unchanged ?
$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$alpha = ($rgb & 0x7F000000) >> 24;
In the next line we are simply converting the RGB values to HSL with rgb2hsl($r, $g, $b); and assigning it to list($h, $s, $l). The point I am stuck at the moment are these lines.
$s += $saturationPercentage / 100;
if($s > 1) $s--;
I understand the syntax, but not sure how I handle these or if they are even required. If not an answer helpful hints / suggestions would be great. I am trying the code on this image to bring down the saturation from 100% to 80%, but I am getting this image as result.
I think you need something like
$s=$s * (100+$saturationPercentage)/100
else you are just adding a constant to each value rather than a percentage of the existing value.
Also, where you decrement 1 if your new saturation exceeds 1, you probaly would be better just setting it to 1.0, i.e. fully saturated like this:
if($s>1)$s=1
otherwise, say the resulting saturation is 1.3 (i.e. very, very saturated) you will make that into 0.3, i.e. very undersaturated, instead of 1.0 (fully saturated).
So, if $s is 0.7, and you add 10%, you will get
$s = 0.7 * (100 + 10)/100
$s = 0.7 * 1.1
$s = 0.77

How to detect "light" colors with PHP

I am working on a dynamic store project and I use a loop to print all color options for a product as color boxes, however I really need to add a "border" to these colors which are light. I tried something like the following but It is very limited, it is actually limited to white color only, it won't catch something like #ddd, #eea... etc
Here is my loop:
foreach($colors as $color) {
$color = trim($color);
if (!empty($color)) {
if (in_array($color, array('white','White','#fff','#FFF','#FFFFFF','#ffffff'))) {
$bordercolor = '#bbb';
} else {
$bordercolor = $color;
}
}
}
Colors is an array from backend like: White, #000, #cc0000, etc. It is not practical to add all exceptions in the if/else condition too, any quick idea?
Transform HTML colour to RGB, then to Hue-Saturation-Lightnes (HSV)
<?php
function HTMLToRGB($htmlCode)
{
if($htmlCode[0] == '#')
$htmlCode = substr($htmlCode, 1);
if (strlen($htmlCode) == 3)
{
$htmlCode = $htmlCode[0] . $htmlCode[0] . $htmlCode[1] . $htmlCode[1] . $htmlCode[2] . $htmlCode[2];
}
$r = hexdec($htmlCode[0] . $htmlCode[1]);
$g = hexdec($htmlCode[2] . $htmlCode[3]);
$b = hexdec($htmlCode[4] . $htmlCode[5]);
return $b + ($g << 0x8) + ($r << 0x10);
}
function RGBToHSL($RGB) {
$r = 0xFF & ($RGB >> 0x10);
$g = 0xFF & ($RGB >> 0x8);
$b = 0xFF & $RGB;
$r = ((float)$r) / 255.0;
$g = ((float)$g) / 255.0;
$b = ((float)$b) / 255.0;
$maxC = max($r, $g, $b);
$minC = min($r, $g, $b);
$l = ($maxC + $minC) / 2.0;
if($maxC == $minC)
{
$s = 0;
$h = 0;
}
else
{
if($l < .5)
{
$s = ($maxC - $minC) / ($maxC + $minC);
}
else
{
$s = ($maxC - $minC) / (2.0 - $maxC - $minC);
}
if($r == $maxC)
$h = ($g - $b) / ($maxC - $minC);
if($g == $maxC)
$h = 2.0 + ($b - $r) / ($maxC - $minC);
if($b == $maxC)
$h = 4.0 + ($r - $g) / ($maxC - $minC);
$h = $h / 6.0;
}
$h = (int)round(255.0 * $h);
$s = (int)round(255.0 * $s);
$l = (int)round(255.0 * $l);
return (object) Array('hue' => $h, 'saturation' => $s, 'lightness' => $l);
}
$colour = '#F12346';
$rgb = HTMLToRGB($colour);
$hsl = RGBToHSL($rgb);
var_dump($hsl);
Usage:
$colour = '#F12346';
$rgb = HTMLToRGB($colour);
$hsl = RGBToHSL($rgb);
if($hsl->lightness > 200) {
// this is light colour!
}
Source:
http://www.caperna.org/computing/repository/hsl-rgb-color-conversion-php
Demo:
http://codepad.org/X7KV4n4n
What I'd do in this situation is detect the lightness of the color using HSL, and compare that against a certain percentage. For example, the lightness attribute in the HSL algorithm takes the chroma (M - m where M is the largest RGB value and m is the smallest RGB value) and divides that by 2.
function lightness($R = 255, $G = 255, $B = 255) {
return (max($R, $G, $B) + min($R, $G, $B)) / 510.0; // HSL algorithm
}
The above function would return a percentage of how light the color you've selected is (simple hex -> rgb conversions are required for this also, but that should be pretty easy). The reason I divided by 510 instead of 2 is because in order to get the percentage after dividing by 2, you divide by 255. To make it faster you can simply say: (x / 2) / 255 = x / 510. Then I'd compare the value returned by the above function to, say, 80%.
$r = hexdec($hex[0].$hex[1]);
$g = hexdec($hex[2].$hex[3]);
$b = hexdec($hex[4].$hex[5]);
if(lightness($r, $g, $b) >= .8) {
// add border
} else {
// no border
}
In addition to other formulas given by other answers, you may want to consider Luma.
function luma($r, $g, $b)
{
return (0.2126 * $r + 0.7152 * $g + 0.0722 * $b) / 255;
}
$l = luma(0, 15, 255);
Values closer to 0 will be darker. Values closer to 1 will be lighter.
Short way if you have RGB color as hexadecimal string:
$hexRGB = "4488BB";
if(hexdec(substr($hexRGB,0,2))+hexdec(substr($hexRGB,2,2))+hexdec(substr($hexRGB,4,2))> 381){
//bright color
}else{
//dark color
}
Note: The thereshold 381 is the sum of the values at average level. If you want the thereshold to be lighter or darker, upper or lower 381 in the range 1 - 765.
There also is a simpler way with even less code:
<?php
//Functions
function getRGB($colorCode) {
//Turn html color code into RGB
$var_R = substr($colorCode, 0, 2);
$var_G = substr($colorCode, 2, 2);
$var_B = substr($colorCode, 4, 2);
//Get Hex values
$val_R = hexdec($var_R);
$val_G = hexdec($var_G);
$val_B = hexdec($var_B);
//Red is seen as light too, gets fixed with this
$remRed = hexdec('99');
if ($val_R > $remRed) {
$RGB = $val_G.' '.$val_B;
} else {
$RGB = $val_R.' '.$val_G.' '.$val_B;
}
return $RGB;
}
function getHSL($R = 255, $G = 255, $B = 255) {
$hsl = (max($R, $G, $B) + min($R, $G, $B)) / 510.0;
return $hsl;
}
?>
Now the calling:
$color = 0000FF; //Blue
$RGBcode = getRGB($color); //Returns 0 0 255
$RGBcode = str_replace(' ', ', ', $RGBcode); //Replaces an empty space with a ,
$val_HSL = getHSL($RGBcode); //Returns value from 0.5 to 1
if ($val_HSL >= 0.8) {
//Reject color
} else {
//Accept Color
$color = '#'.$color; //Sets it to html: #0000FF
}
Here’s a short derived version from #Luca C.’s answer for colors in HEX format (e.g. #FFFFFF or #FFF)
<?php
function isDark($hex){
$average = 381; // range 1 - 765
if(strlen(trim($hex)) == 4){
$hex = "#" . substr($hex,1,1) . substr($hex,1,1) . substr($hex,2,1) . substr($hex,2,1) . substr($hex,3,1) . substr($hex,3,1);
}
return ((hexdec(substr($hex,1,2))+hexdec(substr($hex,3,2))+hexdec(substr($hex,5,2)) < $average) ? true : false);
}
var_dump( isDark("#000000") ); // bool(true)
?>

Check pixels pattern

How can I check for a pixel pattern in PHP?
I mean I wanna use as condition that pixel A has xxx value and the following pixel B has another value yyy.
This is what I wrote:
$img = imagecreatefrompng("myimage.png");
$w = imagesx($img);
$h = imagesy($img);
for($y=0;$y<$h;$y++) {
for($x=0;$x<$w;$x++) {
$rgb = imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
echo "#".$r.$g.$b.",";
$pixel = $r.$g.$b;
if ($pixel == "481023" and $pixel+1???
}
echo "<br />\r\n";
}
I'd like to ask also if I can speed up the whole thing by incrementing the $x value by 2 every for cycle. This because I have a pattern of 2 pixels, maybe I can use something like:
for($x=0;$x<$w;$x+2) {
//...
if ($pixel == "xxx") {//check the following pixel}
else if ($pixel == "yyy") {//check the previous pixel}
}
You might want to define a function like:
function getpixelat($img,$x,$y) {
$rgb = imagecolorat($img,$x,$y);
$r = dechex(($rgb >> 16) & 0xFF);
$g = dechex(($rgb >> 8) & 0xFF);
$b = dechex($rgb & 0xFF);
return $r.$g.$b;
}
Notice the dechex - you need this if you want it to look like an HTML colour code. Otherwise "white" would be 255255255 instead of ffffff and you'd also get ambiguous colours - is 202020 a dark gray (20,20,20) or "red with a slight hint of blue" (202,0,20)?
Once you have this, it should be a simple matter:
for( $y=0; $y<$h; $y++) {
for( $x=0; $x<$w; $x++) {
$pixel = getpixelat($img,$x,$y);
if( $pixel == "481023" && getpixelat($img,$x+1,$y) == "998877") {
// pattern! Do something here.
$x++; // increment X so we don't bother checking the next pixel again.
}
}
}

How do I make a lighter version of a colour using PHP?

Hello fellow earthlings. A quesion about RGB color and its usefulness in a simple tiny php code:
Imagine I have variable $colorA containning a valid six char color. say B1B100, a greenish natural color. Now If I would like to make a new color from that, which is, say, ten steps lighter thatn that original color, roughly.
$colorA = B1B100 // original color
php code with little color engine lightening stuff up goes here
$colorB = ?????? // original color lightened up
Is there a php ready function that KNOWS rgb colors something like
php function RGB ( input color, what to do, output color)
Where what to do could be +/- 255 values of brightness etc etc.
Is something like this already possible or am I day dreaming?
rgb-hsl($colorA, +10, $colorB);
If this does not exist, what would be the shortest code for doing this? Suggestions, code or ideas are all answers to me. Thanks.
This SO question has a full-blown PHP script that can convert a RGB to a HSL colour, and increase its H component of a HSL colour - it should be trivial to change to increase L instead.
In general if you want a lighter shade of a particular colour, the most accurate process is to convert from RGB to HSL (or HSV), change the 'L' (or 'V') value which represents lightness, and then convert back to RGB.
This will preserve the "hue", which represents where the colour sits on the spectrum, but change the "tint" (if lightening) or "shade" (if darkening) of that colour.
See http://en.wikipedia.org/wiki/HSL_and_HSV for more information.
On this website: http://www.sitepoint.com/forums/showthread.php?t=586223 they are talking about this code which is originally made by opensource Drupal. Seems to work fine in PHP!?
Now, how do I now indermingle myself with this code and change the lightness of an HSL value, before its outputted as RGB again?
<?php
### RGB >> HSL
function _color_rgb2hsl($rgb) {
$r = $rgb[0]; $g = $rgb[1]; $b = $rgb[2];
$min = min($r, min($g, $b)); $max = max($r, max($g, $b));
$delta = $max - $min; $l = ($min + $max) / 2; $s = 0;
if ($l > 0 && $l < 1) {
$s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
}
$h = 0;
if ($delta > 0) {
if ($max == $r && $max != $g) $h += ($g - $b) / $delta;
if ($max == $g && $max != $b) $h += (2 + ($b - $r) / $delta);
if ($max == $b && $max != $r) $h += (4 + ($r - $g) / $delta);
$h /= 6;
} return array($h, $s, $l);
}
### HSL >> RGB
function _color_hsl2rgb($hsl) {
$h = $hsl[0]; $s = $hsl[1]; $l = $hsl[2];
$m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
$m1 = $l * 2 - $m2;
return array(_color_hue2rgb($m1, $m2, $h + 0.33333),
_color_hue2rgb($m1, $m2, $h),
_color_hue2rgb($m1, $m2, $h - 0.33333));
}
### Helper function for _color_hsl2rgb().
function _color_hue2rgb($m1, $m2, $h) {
$h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
if ($h * 2 < 1) return $m2;
if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
return $m1;
}
### Convert a hex color into an RGB triplet.
function _color_unpack($hex, $normalize = false) {
if (strlen($hex) == 4) {
$hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
} $c = hexdec($hex);
for ($i = 16; $i >= 0; $i -= 8) {
$out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1);
} return $out;
}
### Convert an RGB triplet to a hex color.
function _color_pack($rgb, $normalize = false) {
foreach ($rgb as $k => $v) {
$out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8));
}return '#'. str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
}
/* $testrgb = array(0.2,0.75,0.4); //RGB to start with
print_r($testrgb); */
print "Hex: ";
$testhex = "#b7b700";
print $testhex;
$testhex2rgb = _color_unpack($testhex,true);
print "<br />RGB: ";
var_dump($testhex2rgb);
print "<br />HSL color module: ";
$testrgb2hsl = _color_rgb2hsl($testhex2rgb); //Converteren naar HSL
var_dump($testrgb2hsl);
print "<br />RGB: ";
$testhsl2rgb = _color_hsl2rgb($testrgb2hsl); // En weer terug naar RGB
var_dump($testhsl2rgb);
print "<br />Hex: ";
$testrgb2hex = _color_pack($testhsl2rgb,true);
var_dump($testrgb2hex);
?>
PHP does have a couple image manipulation libraries. Either GD or Imagemagick
EDIT: I jumped the gun, these libraries do not have direct PHP color manipulation functions - I honestly assumed they did of a sort after seeing a lot of the things they can do with images via PHP. They do accomplish a lot of cool things. Here's one guy's example.

PHP using imageline and XOR

I am trying to use the image GD library to draw lines using an XOR filter. I have not been able to find an easy way to do this so a line being drawn "flips" white to black and vice-versus. Any solutions?
I'm pretty sure that it's not possible to draw the XOR line with built-in imageline PHP function. Though you can draw it yourself with imagesetpixel and custom line drawing algorithm. For example something like this can work (Bresenham Line Algorythm for PHP):
function line($im,$x1,$y1,$x2,$y2) {
$deltax=abs($x2-$x1);
$deltay=abs($y2-$y1);
if ($deltax>$deltay) {
$numpixels=$deltax+1;
$d=(2*$deltay)-$deltax;
$dinc1=$deltay << 1; $dinc2=($deltay-$deltax) << 1;
$xinc1=1; $xinc2=1;
$yinc1=0; $yinc2=1;
} else {
$numpixels=$deltay+1;
$d=(2*$deltax)-$deltay;
$dinc1=$deltax << 1; $dinc2=($deltax-$deltay)<<1;
$xinc1=0; $xinc2=1;
$yinc1=1; $yinc2=1;
}
if ($x1>$x2) {
$xinc1=-$xinc1;
$xinc2=-$xinc2;
}
if ($y1>$y2) {
$yinc1=-$yinc1;
$yinc2=-$yinc2;
}
$x=$x1;
$y=$y1;
for ($i=0;$i<$numpixels;$i++) {
$color_current = imagecolorat ( $im, $x, $y );
$r = ($color_current >> 16) & 0xFF;
$g = ($color_current >> 8) & 0xFF;
$b = $color_current & 0xFF;
$color = imagecolorallocate($im, 255 - $r, 255 - $g, 255 - $b);
imagesetpixel($im,$x,$y,$color);
if ($d<0) {
$d+=$dinc1;
$x+=$xinc1;
$y+=$yinc1;
} else {
$d+=$dinc2;
$x+=$xinc2;
$y+=$yinc2;
}
}
return ;
}
Function perfectly works for images, created from files.

Categories