Calculate the closest colourblind-friendly colour? - php

How can I calculate the closest colourblind-friendly colour from a HEX colour code like #0a87af or from the three RGB values (0-255).
I'm searching for an efficient way to calculate or do this so I can implement it in PHP or Python and the algorithm can be used for better website accessibility for colourblind people.

As the others mentionned in their comments/answer, the contrast between two colours will be of importance.
The W3 already created a method defining a minimum contrast between colours in order to pass dfferent levels of accessibility.
They provide the description here and the formula to calculate it is on the same page, at the bottom, here :
contrast ratio = (L1 + 0.05) / (L2 + 0.05)
For this apparently simple formula, you will need to calculate the relative luminance noted L1 and L2 of both colours using an other formula that you find here :
L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:
if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4
and RsRGB, GsRGB, and BsRGB are defined as:
RsRGB = R8bit/255
GsRGB = G8bit/255
BsRGB = B8bit/255
The minimum contrast ratio between text and background should be of 4.5:1 for level AA and 7:1 for level AAA. This still leaves room for creation of nice designs.
There is an example of implementation in JS by Lea Verou.
This won't give you the closest color as you asked, because on a unique background there will more than one front colour giving the same contrast result, but it's a standard way of calculating contrasts.

A single color is not a problem for color-blind users (unless you want to transport a very specific meaning of that color tone); the difference between colors is.
Given two or more colors, you can convert them to HLS using colorsys and check whether the difference in Lightness is sufficient. If the difference is too small, increase it, like this:
import colorsys
import re
def rgb2hex(r, g, b):
return '#%02x%02x%02x' % (r, g, b)
def hex2rgb(hex_str):
m = re.match(
r'^\#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$', hex_str)
assert m
return (int(m.group(1), 16), int(m.group(2), 16), int(m.group(3), 16))
def distinguish_hex(hex1, hex2, mindiff=50):
"""
Make sure two colors (specified as hex codes) are sufficiently different.
Returns the two colors (possibly changed). mindiff is the minimal
difference in lightness.
"""
rgb1 = hex2rgb(hex1)
rgb2 = hex2rgb(hex2)
hls1 = colorsys.rgb_to_hls(*rgb1)
hls2 = colorsys.rgb_to_hls(*rgb2)
l1 = hls1[1]
l2 = hls2[1]
if abs(l1 - l2) >= mindiff: # ok already
return (hex1, hex2)
restdiff = abs(l1 - l2) - mindiff
if l1 >= l2:
l1 = min(255, l1 + restdiff / 2)
l2 = max(0, l1 - mindiff)
l1 = min(255, l2 + mindiff)
else:
l2 = min(255, l2 + restdiff / 2)
l1 = max(0, l2 - mindiff)
l2 = min(255, l1 + mindiff)
hsl1 = (hls1[0], l1, hls1[2])
hsl2 = (hls2[0], l2, hls2[2])
rgb1 = colorsys.hls_to_rgb(*hsl1)
rgb2 = colorsys.hls_to_rgb(*hsl2)
return (rgb2hex(*rgb1), rgb2hex(*rgb2))
print(distinguish_hex('#ff0000', '#0000ff'))

Contrast-Finder is an open source online tool (written by Open-S and M. Faure) that, given foreground and background colors, will calculate the contrast ratio and if it's insufficient according to WCAG formula, will give you a bunch of background OR foreground colors with sufficient contrast ratio and thus options, using different algorithms (you must tell it if you want to keep the foreground color or the background color and if you want contrast ratio higher than 4.5:1 or 3:1 - level AA - or 7:1 / 4.5:1 - level AAA).
It's pretty spot on for many couples of colors.
Sources - in Java - are on GitHub.
Note: as already written in other answers, colourblind people ("people with colour deficiencies") are only a part of people concerned by the choice of colors: partially sighted people also are. And when a webdesigner chooses #AAA on #FFF, it's a problem for many people without any loss of sight or colour perception; they just've a shiny Retina® screen in non-optimal light conditions... :p

Related

Rotate K coplanar points to a plane parallel to x,y plane

I'm working in php with 3D geometries(not the best choice,I know...).
I have K coplanar 3D points, also with x,y,z value. Together they form a polygon. I need to triangulate this polygon. I have already a working delaunay traingulation function which works for 2D Polygons.
So I want to rotate the given points, so that they lay on a plane parallel to the x,y plane. After that I can triangulated it using the x,y values. The following pseudocode shall describe how I want to get to this goal.
I build up the following code with reference on this (I'm usign the answer accepted from the OP): https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d, but it doesn't work as I expected. In order to know if it worked, every mapped point shall then have the same 'z' value.
Here is the question, how do I get the correct rotation matrix? Or did I made a conceptual mistake?
function matrixRotationMapping(Point $p, Point $q, Point $r)
{
$normalPolygon =calculatePlaneNormal($p, $q, $r);
$v = crossProduct($normalPolygon, new Point(0, 0, 1));
$c = dotProduct($normalPolygon, new Point(0, 0, 1));
$matrix = buildRotationMatrix($v, $c);
return $matrix;
}
function buildRotationMatrix($v, $c)
{
$R2 = new Matrix(array(array(1, -$v->z, $v->y), array($v->z, 1, -$v->x), array(-$v->y, $v->x, 1)));
$costant = 1/(1+$c);
$R3 = multiplyMatrices($R2, $R2);
$R3 = multiplyMatricesWithFactor($R3, $costant);
$finalMatrix = sumMatrices($R2, $R3);
return $finalMatrix;
}
function calc2DMapping($points)
{
$rotationMatrix = matrixRotationMapping($points[0], $points[1], $points[2]);
foreach($points as $point)
{
$mappedPoint = $rotationMatrix->multiplyWithPoint($point);
$mappedPoints[] = new MappedPoint($mappedPoint);
}
}
I found another helpful description of the problem, but I wasn't able to implement it: Mapping coordinates from plane given by normal vector to XY plane
Thanks in advance for your attention.
You need basis vectors X,Y,Z first. So let take the mid point A and two distant points to it B,C (not on single line) from your data set first. The X,Y should lie in the plane and Z should be normal to it so:
X = B-A // any non zero vector inside plane
X = X / |X| // unit in size
Y = C-A // any non zero vector inside plane
(X.Y) != 0 // but not parallel to X !!!
Y = Y / |Y| // unit in size
Compute normal to the plane your points lie in and correct Y axis.
Z = X x Y // cross product gives you perpendicular vector
Y = Z x X // now all vectors are perpendicular and unit
So feed these 3 vectors to rotation part of your transform matrix and set origin to A. But as you need to go from your data set to the plane local coordinate you need inverse matrix (or use pseudo inverse based on transposing)
Anyway now with the basis vectors you can map your plane parametrically like this:
P(u,v) = A + u*X + v*Y
Where u,v = <-inf,+inf> are surface distances form A in X,Y directions. That can get handy sometimes. If you need to compute u,v from P then exploit dot product:
u = ((P-A).X) = dot(P-A,X)
v = ((P-A).Y) = dot(P-A,Y)
Which can be also used to transform to 2D instead of using matrix ...

php game, formula to calculate a level based on exp

Im making a browser based PHP game and in my database for the players it has a record of that players total EXP or experience.
What i need is a formula to translate that exp into a level or rank, out of 100.
So they start off at level 1, and when they hit say, 50 exp, go to level 2, then when they hit maybe 125/150, level 2.
Basically a formula that steadily makes each level longer (more exp)
Can anyone help? I'm not very good at maths :P
Many formulas may suit your needs, depending on how fast you want the required exp to go up.
In fact, you really should make this configurable (or at least easily changed in one central location), so that you can balance the game later. In most games these (and other) formulas are determined only after playtesting and trying out several options.
Here's one formula: First level-up happens at 50 exp; second at 150exp; third at 300 exp; fourth at 500 exp; etc. In other words, first you have to gather 50 exp, then 100 exp, then 150exp, etc. It's an Arithmetic Progression.
For levelup X then you need 25*X*(1+X) exp.
Added: To get it the other way round you just use basic math. Like this:
y=25*X*(1+X)
0=25*X*X+25*X-y
That's a standard Quadratic equation, and you can solve for X with:
X = (-25±sqrt(625+100y))/50
Now, since we want both X and Y to be greater than 0, we can drop one of the answers and are left with:
X = (sqrt(625+100y)-25)/50
So, for example, if we have 300 exp, we see that:
(sqrt(625+100*300)-25)/50 = (sqrt(30625)-25)/50 = (175-25)/50 = 150/50 = 3
Now, this is the 3rd levelup, so that means level 4.
If you wanted the following:
Level 1 # 0 points
Level 2 # 50 points
Level 3 # 150 points
Level 4 # 300 points
Level 5 # 500 points etc.
An equation relating experience (X) with level (L) is:
X = 25 * L * L - 25 * L
To calculate the level for a given experience use the quadratic equation to get:
L = (25 + sqrt(25 * 25 - 4 * 25 * (-X) ))/ (2 * 25)
This simplifies to:
L = (25 + sqrt(625 + 100 * X)) / 50
Then round down using the floor function to get your final formula:
L = floor(25 + sqrt(625 + 100 * X)) / 50
Where L is the level, and X is the experience points
It really depends on how you want the exp to scale for each level.
Let's say
LvL1 : 50 Xp
Lvl2: LvL1*2=100Xp
LvL3: LvL2*2=200Xp
Lvl4: LvL3*2=400Xp
This means you have a geometric progression
The Xp required to complete level n would be
`XPn=base*Q^(n-1)`
In my example base is the inital 50 xp and Q is 2 (ratio).
Provided a player starts at lvl1 with no xp:
when he dings lvl2 he would have 50 total Xp
at lvl3 150xp
at lvl4 350xp
and so forth
The total xp a player has when he gets a new level up would be:
base*(Q^n-1)/(Q-1)
In your case you already know how much xp the player has. For a ratio of 2 the formula gets simpler:
base * (2^n-1)=total xp at level n
to find out the level for a given xp amount all you need to do is apply a simple formula
$playerLevel=floor(log($playerXp/50+1,2));
But with a geometric progression it will get harder and harder and harder for players to level.
To display the XP required for next level you can just calculate total XP for next level.
$totalXpNextLevel=50*(pow(2,$playerLevel+1)-1);
$reqXp=$totalXpNextLevel - $playerXp;
Check start of the post:
to get from lvl1 -> lvl2 you need 50 xp
lvl2 ->lvl3 100xp
to get from lvl x to lvl(x+1)
you would need
$totalXprequired=50*pow(2,$playerLevel-1);
Google gave me this:
function experience($L) {
$a=0;
for($x=1; $x<$L; $x++) {
$a += floor($x+300*pow(2, ($x/7)));
}
return floor($a/4);
}
for($L=1;$L<100;$L++) {
echo 'Level '.$L.': '.experience($L).'<br />';
}
It is supposed the be the formula that RuneScape uses, you might me able to modify it to your needs.
Example output:
Level 1: 0
Level 2: 55
Level 3: 116
Level 4: 184
Level 5: 259
Level 6: 343
Level 7: 435
Level 8: 536
Level 9: 649
Level 10: 773
Here is a fast solution I used for a similar problem. You will likely wanna change the math of course, but it will give you the level from a summed xp.
$n = -1;
$L = 0;
while($n < $xp){
$n += pow(($L+1),3)+30*pow(($L+1),2)+30*($L+1)-50;
$L++;
}
echo("Current XP: " .$xp);
echo("Current Level: ".$L);
echo("Next Level: " .$n);
I take it what you're looking for is the amount of experience to decide what level they are on? Such as:
Level 1: 50exp
Level 2: 100exp
Level 3: 150exp ?
if that's the case you could use a loop something like:
$currentExp = x;
$currentLevel;
$i; // initialLevel
for($i=1; $i < 100; $i *= 3)
{
if( ($i*50 > $currentExp) && ($i < ($i+1)*$currentExp)){
$currentLevel = $i/3;
break;
}
}
This is as simple as I can make an algorithm for levels, I haven't tested it so there could be errors.
Let me know if you do use this, cool to think an algorithm I wrote could be in a game!
The original was based upon a base of 50, thus the 25 scattered across the equation.
This is the answer as a real equation. Just supply your multiplier (base) and your in business.
$_level = floor( floor( ($_multipliter/2)
+ sqrt( ($_multipliter^2) + ( ($_multipliter*2) * $_score) )
)
/ $_multipliter
) ;

How to find the % of a value on a Y axis of a LOG graph?

Wow, this one is though!
I'm trying to find in PHP the % of a value relative to the Y axis. If we refer to this graph : http://en.wikipedia.org/wiki/Semi-log_graph (2009 outbreak of influenza A), let's say that I want to find what % is a value "256" on the chart.
Visually, it's easy : it's a bit more than a 1/3 or 33%. If we look at the 1024 value, it's around 50% of the height of the Y axis. 131072 would be 100%.
So how do I calculate this with PHP?
Let's take this graph and take X = day 0 and Y = 256. What is 256 as a % of Y ?
Thanks a lot to anyone can compute this baby :)
percent = 100 * ( log(y) - log(y1) ) / ( log(y2) - log(y1) )
where
y = value
y1 = smallest value in y-axis
y2 = largest value in y-axis.
when y1 = 1.0 then you can simplify the other answers given here (since log(1)=0 by definition)
percent = 100 * log(y)/log(y2)
Note that not all log charts have 1.0 as the lowest value.
ln(131072) = 11.783 is 100%
ln(1024) = 6.931 is 58.824%
in PHP, that function is called log()
no need to set the base, as you are dividing them to find the relative value.
Alex got it, but to generalize for you and PHPize
logPercent = log(x) / log(top) * 100;
If you take the log of your max y value (131072 in your case) and the log of your y value (256), you end up with a height and a y value which is linear in relation to your drawn axis. you can divide them to get a decimal of the height and times by 100 for %:
using log base 2 seen as it gives integers (though any base should be fine).
log(256) / log(131072) = 8/17 = 0.47 = 47%
in php:
(log(256, 2) / log(131072, 2))*100;

Perspective transformation with GD

How can I apply a perspective transformation on an image
using only the PHP GD library?
I don't want to use a function someone else made I want to UNDERSTAND what's going on
I honestly don't know how to describe mathematically a perspective distortion. You could try searching the literature for that (e.g. Google Scholar). See also in the OpenGL documentation, glFrustum.
EDIT: Interestingly, starting with version 8, Mathematica has a ImagePerspectiveTransformation. In the relevant part, it says:
For a 3*3 matrix m, ImagePerspectiveTransformation[image,m] applies LinearFractionalTransform[m] to image.
This is a transformation that, for some a (matrix), b (vector), c (vector) and d (scalar), transforms the vector r to (a.r+b)/(c.r+d). In a 2D situation, this gives the homogeneous matrix:
a_11 a_12 b_1
a_21 a_22 b_2
c_1 c_2 d
To apply the transformation, you multiply this matrix by the column vector extended with z=1 and then take the first two elements of the result and divide them by the third:
{{a11, a12, b1}, {a21, a22, b2}, {c1, c2, d}}.{{x}, {y}, {1}} // #[[
1 ;; 2, All]]/#[[3, 1]] & // First /# # &
which gives:
{(b1 + a11 x + a12 y)/(d + c1 x + c2 y),
(b2 + a21 x + a22 y)/(d + c1 x + c2 y)}
With the example:
a = {{0.9, 0.1}, {0.3, 0.9}}
b = {0, -0.1}
c = {0, 0.1}
d = 1
You get this transformation:
im = Import["/home/cataphract/Downloads/so_q.png"];
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];
(*transf=TransformationFunction[{{0.9, 0.1, 0.}, {0.3,
0.9, -0.1}, {0., 0.1, 1.}}] -- let's expand this:*)
transf = {(0.9 x + 0.1 y)/(1.+ 0.1 y), (-0.1 + 0.3 x + 0.9 y)/(
1. + 0.1 y)} /. {x -> #[[1]], y -> #[[2]]} &;
ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1},
ColorFunction -> (orfun[1 - #4, #3] &),
Mesh -> None,
FrameTicks -> None,
Axes -> False,
ImageSize -> 200,
PlotRange -> All,
Frame -> False
]
Once you have a map that describes the position of a point of the final image in terms of a point in the original image, it's just a matter of finding its value for each of the points in the new image.
There's one additional difficulty. Since an image is discrete, i.e., has pixels instead of continuous values, you have to make it continuous.
Say you have a transformation that doubles the size of an image. The function to calculate a point {x,y} in the final image will look for point {x/2, y/2} in the original. This point doesn't exist, because images are discrete. So you have to interpolate this point. There are several possible strategies for this.
In this Mathematica example, I do a simple 2D rotation and use a degree-1 spline function to interpolate:
im = Import["d:\\users\\cataphract\\desktop\\img.png"]
orfun = BSplineFunction[ImageData[im], SplineDegree -> 1];
transf = Function[{coord}, RotationMatrix[20. Degree].coord];
ParametricPlot[transf[{x, y}], {x, 0, 1}, {y, 0, 1},
ColorFunction -> (orfun[1 - #4, #3] &), Mesh -> None,
FrameTicks -> None, Axes -> None, ImageSize -> 200,
PlotRange -> {{-0.5, 1}, {0, 1.5}}]
This gives:
PHP:
For the interpolation, google for "B-spline". The rest is as follows.
First choose a referential for the original image, say if the image is 200x200, pixel (1,1) maps (0,0) and pixel (200,200) maps to (1,1).
Then you have to guess where your final image will land when the transformation is applied. This depends on the transformation, you can e.g. apply it to the corners of the image or just guess.
Say you consider the mapped between (-.5,0) and (1, 1.5) like I did and that your final image should be 200x200 also. Then:
$sizex = 200;
$sizey = 200;
$x = array("min"=>-.5, "max" => 1);
$y = array("min"=>0, "max" => 1.5);
// keep $sizex/$sizey == $rangex/$rangey
$rangex = $x["max"] - $x["min"];
$rangey = $y["max"] - $y["min"];
for ($xp = 1; $xp <= $sizex; $xp++) {
for ($yp = 1; $yp <= $sizey; $yp++) {
$value = transf(
(($xp-1)/($sizex-1)) * $rangex + $x["min"],
(($yp-1)/($sizey-1)) * $rangey + $y["min"]);
/* $value should be in the form array(r, g, b), for instance */
}
}

"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