Find Colour Range - php

I want to find a range of colours from the value of one RGB value
If I was given rgb(0,100,200) for example it would give me everything between rgb(0,0,255) and rgb(0,255,255). However not rgb(255,0,255).
Similarly rgb(150,50,0). Return: rgb(255,0,0) and rgb(255,255,0). Not rgb(255,0,255).
Making sense?
Im using PHP

The algorithm you explain is basically: "A color consists of two RGB components. Let the strongest RGB component be S and the other component O. Create 255 variations where you let S be (255) and O ranges from 0 to 255."
E.g. all of the below example yield the same result:
a) rgb(0,100,200)
b) rgb(0,199,200)
c) rgb(0,254,255)
d) rgb(0,1,2)
Result: rgb(0,[0-255],255)
This means you only have a 6 variations.
Red is the largest component, Green next largest => rgb(255,[0-255],0)
Red is the largest component, Blue next largest => rgb(255,0,[0-255])
Green is the largest component, Red next largest => rgb([0-255],255,0)
Green is the largest component, Blue next largest => rgb([0-255],0,255)
Blue is the largest component, Red next largest => rgb([0-255],0,255)
Blue is the largest component, Green next largest => rgb(0,[0-255],255)
The intention of your algorithm is not clear, so I am guessing your use case is actually different from what you explain. It does not handle colors with either 1 or 3 components (most colors actually).
If your goal is to find similar colors (e.g. a color in a certain distance) there are better approaches. For example you could convert your colors to a HSV color space (Hue, Saturation, Value) and then say that the color is similar if the either of the components H, S or V are +/- 10 steps from your original.
E.g.:
rgb(255,100,0) => hsv(24,100,100)
Your range is then hsv([14-34],[90-110],[90-110])

Hm, not sure I understand this correctly, but I think you are saying that one of the numbers is higher than the other two, and one of the values is always zero. If this is the case, you should be able to use a simple if-else statement similar to
if (r > g && r > b) {
if (g > 0) {
color1 = rgb(255, 0, 0);
color2 = rgb(255, 255, 0);
}
else {
color1 = rgb(255, 0, 0);
color2 = rgb(255, 0, 255);
}
}
else if (r < g && g > b) {
.
.
.
}
Hope that will help you out with your problem.

Related

How to map a range of hex values to a color

I need to get the most dominant color in an image and succeeded in finding the hex value(RGB) corresponding to it. The next thing is to map hex values to common colors (like red,yellow,green,blue,purple,pink,white,grey,black,brown etc). So basically what I need is a way to map range of hex values to a particular color.
For example #ff5050, #ff1a1a, #e60000 etc are red. So if #ff5050 is given as input the result must be red. In this way all hex values must be matched to some common colors that was mentioned above.
How can one achieve this?
The programming language that I prefer is php.
Or more engineering approach. Devide RGB space into 8 subspaces and make color name approximation. Then you just need to find out in which subspace your color is.
$colorNames = array(
array(
array(
'black', // 0 0 0
'blue' // 0 0 1
),
array(
'green', // 0 1 0
'cyan' // 0 1 1
)
),
array(
array(
'red', // 1 0 0
'violet' // 1 0 1
),
array(
'yellow',// 1 1 0
'white' // 1 1 1
)
)
);
function GetColorName($r, $g, $b)
{
global $colorNames;
echo $r_appx = (int)($r/0x80);
echo $g_appx = (int)($g/0x80);
echo $b_appx = (int)($b/0x80);
return $colorNames[$r_appx][$g_appx][$b_appx];
}
echo GetColorName(0xAA,0x40,0x40); // red
echo GetColorName(0x40,0xAA,0x40); // green
echo GetColorName(0x40,0x40,0xAA); // blue
echo GetColorName(0xAA,0x40,0xAA); // violet
echo GetColorName(0xAA,0xAA,0x40); // yellow
echo GetColorName(0x00,0xAA,0xAA); // cyan
echo GetColorName(0x40,0x40,0x40); // black
echo GetColorName(0xAA,0xAA,0xAA); // white
Similarly you can easily devide RGB space into 27 subspaces if you need more colors.
You can get picture histogram in PHP. You can find more info and code example here: http://php.net/manual/en/imagick.getimagehistogram.php
To map your color to html name, you can go through the array of colors and calculate color distance with color you are looking for. The color with the smallest distance is the one which will give the best html name. You can find more resources about color distance here: https://en.m.wikipedia.org/wiki/Color_difference
This might take huge amount of time every time you process every new picture so BETTER SOLUTION:
You are in RGB color space. This means you have 256x256x256 different colors. Each color is defined by RGB coordinates - http://www.w3schools.com/colors/colors_names.asp. For examle 'aqua' has RGB coordinates (0,255,255). Your goal is to name all colors in RGB space by a name, but you dont need unique name for very single color in RGB space because you are not able to distinguish every detail by your eye (for example (0,0,1) and (0,0,2) would look the same). Therefore you specify a list of color names you want:
$colorNames = array(
[0] => 'white', //#FFFFFF
[1] => 'red', //#FF0000
...
);
Now, for quick mapping of color C, given by coordinates (r,g,b), to color name, you have to lookup, what index in $colorNames array corresponds to color C. For this, you can prepare a file in advance, which has size of 256*256*256 = 16MB. Each byte represents one point in RGB space. The value of the byte is the index of color in $colorNames array to which the color is similar the most. How to create the file (pseudo code):
for every value of R coordinate
for every value of G coordinate
for every value of B coordinate
find which color name in $colorName has the smallest distance to coordinates (R,G,B)
store the index for this color name to file
Now you have file which holds all indexes for any color. This means that all you have to do now, to map any color to name, is read one byte in this file and look into the array for the name of the color.
open mapping file
read one byte on position (R*256*256 + G*256 + B)
this byte is the 'index' in colorNames array
read the color name (colorNames['index'])

Map value to color scale

I have a list of values which should be plotted to a map with a certain color.
The plotting to the map is already done, but I need to figure out a way to map the value n to a color that represents its value.
An example and my solution so far is to normalize the values based on the min and max and then assign them to hex color 0 for the lowest and 255 for the highest. This of course limits my self to the grey scale. Here is the code:
$color = ($value / $max) * 255 // (min is zero)
But how to do this if the values should go from blue to red for instance?
Is there any common libraries or tools that can solve this? So far I haven't been able to locate any.
There might be libs to do that. However let's get a short warm up into the general principles. In general you have following options:
A predefined color index, e.g. $coloridx=array(0=>'#FFFFFF',1=>'#FFEE00',...);
Any algorithm, e.g. linear gradient, which is basically an iterations based adaption of all three RGB channels (R = red, G = green, B = blue).
A combination of both, which usually puts the result of any complex algorithm to the color index and then goes from there.
If you include algorithms in your considerations you must understand that there is no true or false. It all depends on what you would like to implement. There might be occasions where it makes sense to render variations of green into n=0..10 and then have red to black in everything beyond n>10. Caps and multipliers help to set accents. Things like that.
One way of implementing a linear gradient would be:
function lineargradient($ra,$ga,$ba,$rz,$gz,$bz,$iterationnr) {
$colorindex = array();
for($iterationc=1; $iterationc<=$iterationnr; $iterationc++) {
$iterationdiff = $iterationnr-$iterationc;
$colorindex[] = '#'.
dechex(intval((($ra*$iterationc)+($rz*$iterationdiff))/$iterationnr)).
dechex(intval((($ga*$iterationc)+($gz*$iterationdiff))/$iterationnr)).
dechex(intval((($ba*$iterationc)+($bz*$iterationdiff))/$iterationnr));
}
return $colorindex;
}
$colorindex = lineargradient(
100, 0, 0, // rgb of the start color
0, 255, 255, // rgb of the end color
256 // number of colors in your linear gradient
);
$color = $colorindex[$value];
I UPDATED the code to add dechex, which feeds back on the comments.
Colors values are representations. Numeric colors as well as hexadecimal colors. A "not grayscale" color contains at least 2 different informations: Red value, green value or blue values may be different. Performing operation on its representation gives wrong result. The 'Mapping" must then be performed on each pieces of information. You need to extract red, green and blue values, perform the mapping seperatly, then build the representation of the result color. Here is a quick helper that use a "min color" and "max color", performs mapping on red, green and blue values, according to the "n" value you need to work with, then return the result color in hexadecimal string. It works for any colors or gray scale color as well.
function linear_color($from, $to, $ratio) {
// normalize ralio
$ratio = $ratio<0?0:($ratio>1?1:$ratio);
// unsure colors are numeric values
if(!is_numeric($from))$from=hexdec($from);
if(!is_numeric($to))$to=hexdec($to);
$rf = 0xFF & ($from >> 0x10);
$gf = 0xFF & ($from >> 0x8);
$bf = 0xFF & $from;
$rt = 0xFF & ($to >> 0x10);
$gt = 0xFF & ($to >> 0x8);
$bt = 0xFF & $to;
return str_pad( dechex(($bf + (($bt-$bf)*$ratio)) + ($gf + (($gt-$gf)*$ratio) << 0x8) + ($rf + (($rt-$rf)*$ratio) << 0x10)), 6,'0',STR_PAD_LEFT);
}
Just specify 2 colors as numeric value or hexadecimal string (without hash!) like this :
$color_from = hexdec('c2c2c2');
$color_to = hexdec('1eb02b');
for($i=-0.2; $i<=1.3; $i+=0.04){
echo '<div style="background-color: #';
echo linear_color($color_from, $color_to, $i);
echo '">';
echo 'Result color when n = <strong>'.$i.'</strong>';
echo '</div>';
}
The other answer was very usefull. I decided to use JS as it reduces server load. The requirements also changed. The bar has to go from Red to white in the middle and then from white to yellow. And if the value is 0 it should be black. Here is my code for anyone who ever encounters a similar situation.
var normalize_base = 2*255;
var no_con_color = "black";
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
function normalize(max, min, value)
{
var normalized = (value - min) / (max - min) ;
return Math.floor(normalized * normalize_base);
}
function value2Color(value)
{
if(value <= 0 || value == undefined)
{
return no_con_color;
}
var g = 0;
var b = 0;
var r = 0;
if(value < 255)
{
r = 255;
g = value;
b = value;
} else {
r = (2*255) - value;
g = 255;
b = (2*255) - value;
}
return "#" + decimalToHex(r) + decimalToHex(g) + decimalToHex(b);
}
There is I2UI for this.
<div data-i2="color:['#404040','#00FF21']">
<span data-i2="rate:1">A</span>
<span data-i2="rate:2">B</span>
<span data-i2="rate:3">C</span>
<span data-i2="rate:4">D</span>
<span data-i2="rate:5">E</span>
</div>
There is a color range: from "Gray" - to "Green". The span element that has lowest rate value get the "Gray" color, the element with the biggest rate get "Green" color.
Thus, the span that are between edges get the color that has direct ratio to its rate.
Also, call JavaScript i2.emph() after the previous HTML have been loaded.
See demo
This is one-function-only code to convert any number from any given range (let's say [0,20]) to particular color where 0 is red, 10 is yellow, 20 is green. You can use any colors and even use combination of 4 colors, so it's red - yellow - green - blue.
See the end of the gist file, where this function is used.
Gist with the code:
Click here

How to decide/check whether some point belongs to a vector shape or not?

I created some map (8000x8000) in photoshop. Every part of the map is created with custom shape tool and can be exported to *.ai file. So i exported one example shape to *.ai file, which has following contents:
%!PS-Adobe-2.0
%%Creator: Adobe Photoshop(TM) Pen Path Export 7.0
%%Title: (map-small-paddington.ai)
%%DocumentNeededResources: procset Adobe_packedarray 2.0 0
%%+ procset Adobe_IllustratorA_AI3 1.0 1
%%ColorUsage: Black&White
%%BoundingBox: 0 0 7998 7998
%%HiResBoundingBox: 0 0 7998.9998 7998.9998
%AI3_Cropmarks: 0 0 7998.9998 7998.9998
%%DocumentPreview: None
%%EndComments
%%BeginProlog
%%IncludeResource: procset Adobe_packedarray 2.0 0
Adobe_packedarray /initialize get exec
%%IncludeResource: procset Adobe_IllustratorA_AI3 1.0 1
%%EndProlog
%%BeginSetup
Adobe_IllustratorA_AI3 /initialize get exec
n
%%EndSetup
0.0 0.0 0.0 1.0 k
0 i 0 J 0 j 1 w 4 M []0 d
%%Note:
%Adobe_Photoshop_Path_Begin:<< /defaultFill false >>
*u
%AI3_Note:<< /operation /union >>
0 XR
4771.1153 4121.9960 m
4855.5878 4111.9732 4957.9582 4086.2029 5003.7736 4060.4311 c
5049.5891 4034.6599 5120.0382 3972.7994 5193.7738 3957.0505 c
5267.5089 3941.3016 5323.5226 3946.6539 5355.9339 3949.0778 C
5356.7559 3955.9301 5358.4651 3964.2284 5361.9409 3972.1987 C
5361.4717 3972.3317 L
5358.0990 3975.3640 5353.5658 3979.5329 5347.8721 3984.9663 c
5333.9035 3998.3003 5250.7315 4090.9969 Y
5162.4795 4165.2807 5134.5423 4193.2165 v
5106.6045 4221.1528 5064.0674 4221.7889 5022.1634 4227.5026 c
4980.2590 4233.2172 4968.8311 4237.6617 4932.0072 4254.1706 c
4895.1828 4270.6786 4843.1201 4292.2642 4827.8818 4298.6139 c
4812.6435 4304.9637 4796.1361 4316.3911 4778.9935 4340.5184 c
4761.8505 4364.6433 4745.9776 4385.5963 4728.8351 4398.9284 c
4723.2744 4403.2528 4716.9076 4407.1833 4712.3291 4410.3572 C
4681.6484 4314.8482 4661.9599 4219.4050 V
4662.8157 4217.2633 4684.8314 4162.2146 4701.6759 4132.7359 C
4710.9826 4122.7135 4771.1153 4121.9960 Y
n
*U
%Adobe_Photoshop_Path_End
%%Trailer
%%EOF
Now let's say I have two points: (4830,3780) which belongs to this shape, and (4480,3130) which doesn't. I know it because I checked it in Photoshop.
But my question is how to check every point I want programmatically? Is there a way to do it in PHP?
So question consists of two parts:
Load and read *.ai file
Check whether point belongs to the shape.
Any help would be appreciated.
The simplest way I can think of to check inside/outside is rasterizing the image and using color.
Create a version of your map where the shape elements are all black, and the background is white. Save that as a JPEG. If you have PHP installed with the GD or Imagemagik extensions, PHP can pick the color out of an image at the coordinates you want:
<?php
$im = imagecreatefromjpeg('map.jpg'); // Import black/white map image
var_dump(isInside($im, 4830, 3780)); // bool(true)
var_dump(isInside($im, 4480, 3130)); // bool(false)
function isInside($im, $x, $y) {
$rgb = imagecolorat($im, $x, $y);
$colors = imagecolorsforindex($im, $rgb);
$value = ($colors['red'] + $colors['green'] + $colors['blue'])/3; // Get average value
return ($value <= 10)? true : false; // If average value is less than 10, this pixel is black (or very close to it), so is inside a shape
}
?>
I'm using a $value <= 10 here rather than $value == 0 since at the edges of your shapes, you'll get a little aliasing where the white background will blend into the black shape. The color value is from zero to 255, so 10/255 is still pretty black, and will make sure that if you target a pixel that's near a shape border, it will still evaluate properly.

Color coding based on number

I want to display a color between red, yellow, green depending on a number between 1 to 100.
1 being green and 100 being red, 50 being yellow. I want to basically create a gradient between that.
So far, I tried:
$r = floor(255 * ($number / 100));
$g = 255 - $r;
And it somewhat does it, but gives me brownish & dark colors, & no yellow at all.
It's because you shouldn't change both channels at once but rise R in the first half and lower G in the second.
Try a function like this:
function GreenYellowRed($number) {
$number--; // working with 0-99 will be easier
if ($number < 50) {
// green to yellow
$r = floor(255 * ($number / 50));
$g = 255;
} else {
// yellow to red
$r = 255;
$g = floor(255 * ((50-$number%50) / 50));
}
$b = 0;
return "$r,$g,$b";
}
To test it:
$output = "";
for ($i = 1; $i <= 100; $i++) {
$rgb = GreenYellowRed($i);
$output .= "<div style='background-color: rgb($rgb)'>$rgb</div>";
}
echo $output;
I've found that dealing with the HSV color model is easier than the RGB model. It helps you easily choose the color you want to work with; with RGB you'd need to understand how different values of R, G and B will combine to give you the color you want/don't want.
Also, this SO question might be useful: How can I cycle through hex color codes in PHP?
I don't know of a mathematical model for a "color curve" that passes through specified RGB color values (e.g. what you describe as green/yellow/red), which would allow you to calculate any intermediate color in that curve. In any case, a model of a function (which is what that would be) is only as good as the data points it needs to fit, so you 'd have to be much more specific than green/yellow/red to get decent results even if someone points out the math.
Remember that we are not interested in mathematical interpolation here, but rather in "color-space interpolation" (a term which I just made up) -- in other words, what would look like a "natural" interpolation to a human.
An easier solution for those of us who do not have the necessary color theory knowledge, and which I 'd suggest, is to pre-select a number of colors with a color picker tool, divide the 0-100 range into as many bands as the colors you picked, and use simple integer division to project from 0-100 to a color band.
Food for thought: Indeed, how does SO decide the color of the upvote count for comments?
Update: I just asked the above over on meta. Let's see...
After a bit of looking, none of the solutions looked pleasing. As stated above, HSV is probably the way to go, since modern browsers can render color with it just fine.
To get a good idea of the colors you are working with, check out this color wheel:
http://www.colorspire.com/rgb-color-wheel/
I want to start with blue, so I use 255 for normalization.
function temp_color($temp){
$start = 40;
$end = 85;
$normal = round(255-((($temp - $start)/($end-$start))*255));
$color = "hsl($normal, 100%, 30%);";
$span = "<span style=\"color: $color\">$temp</span>";
return $span;
}

"Distance" between colours in PHP

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
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) ;
}
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
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;
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.
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...
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.

Categories