Color difference algorithm – part 2
It’s time to see how to calculate difference between two colors.
I am using the CIE1994 formula because it’s quite accurate and not that complex to calculate.
Here it is, assuming I have two colors in Lab format

where

(thanx to Bruce Lindbloom)
First, I am turning the scripts explained at Color difference algorithm into functions, this way:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
function rgb_to_xyz($rgb){ $red = $rgb[0]; $green = $rgb[1]; $blue = $rgb[2]; $_red = $red/255; $_green = $green/255; $_blue = $blue/255; if($_red>0.04045){ $_red = ($_red+0.055)/1.055; $_red = pow($_red,2.4); } else{ $_red = $_red/12.92; } if($_green>0.04045){ $_green = ($_green+0.055)/1.055; $_green = pow($_green,2.4); } else{ $_green = $_green/12.92; } if($_blue>0.04045){ $_blue = ($_blue+0.055)/1.055; $_blue = pow($_blue,2.4); } else{ $_blue = $_blue/12.92; } $_red *= 100; $_green *= 100; $_blue *= 100; $x = $_red * 0.4124 + $_green * 0.3576 + $_blue * 0.1805; $y = $_red * 0.2126 + $_green * 0.7152 + $_blue * 0.0722; $z = $_red * 0.0193 + $_green * 0.1192 + $_blue * 0.9505; return(array($x,$y,$z)); } |
and…
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
function xyz_to_lab($xyz){ $x = $xyz[0]; $y = $xyz[1]; $z = $xyz[2]; $_x = $x/95.047; $_y = $y/100; $_z = $z/108.883; if($_x>0.008856){ $_x = pow($_x,1/3); } else{ $_x = 7.787*$_x + 16/116; } if($_y>0.008856){ $_y = pow($_y,1/3); } else{ $_y = (7.787*$_y) + (16/116); } if($_z>0.008856){ $_z = pow($_z,1/3); } else{ $_z = 7.787*$_z + 16/116; } $l= 116*$_y -16; $a= 500*($_x-$_y); $b= 200*($_y-$_z); return(array($l,$a,$b)); } |
Now, it’s time to determine color difference, using the above formula:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function de_1994($lab1,$lab2){ $c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]); $c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]); $dc = $c1-$c2; $dl = $lab1[0]-$lab2[0]; $da = $lab1[1]-$lab2[1]; $db = $lab1[2]-$lab2[2]; $dh = sqrt(($da*$da)+($db*$db)-($dc*$dc)); $first = $dl; $second = $dc/(1+0.045*$c1); $third = $dh/(1+0.015*$c1); return(sqrt($first*$first+$second*$second+$third*$third)); } |
And you’ll find the difference between colors.
Next time, a real application using all this boring theory… (you will be surprised…)














This post has 11 comments
dim
great snippet … i will love to see a function that can generate lighter or darker color by a given color value through parameter
this will be very handy when we want to auto generate color themes bye given color value by user
How to Choose Colours Procedurally (Algorithms) ยป devmag.org.za
[...] http://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2/ [...]
Eduardo
I trying to transfer this code to pascal. I don’t understan the following operation:
$_red *= 100;
$_green *= 100;
$_blue *= 100;
Could you explain to me?
kpodenphant
This is frickin’ awesome.I ported it to Actionscript.
I tried the RGB distance method which worked ok, but had some errors. Then i tried converting to HSB which worked well for the RGB errors but has some other error.
This algorithm is sweet!
anonyMouse
Eduardo it means that the variable is set to its current value times 100;
i.e. red=2;
red*=100; (red is now 200, all further code that uses the variable “red” will now use 200 rather than 2);
Radek
Thanks for great snippet Emanuele !
Just one point – shouldn’t be used abs(…) for all delta computations in de_1994() ?
Thanks anyway !
Igor
Thanks for explaining this stuff, and for your snippets of code )
I’d like to use those snippets in our project, but in your post you don’t mention it anywhere, so can I ask you if it’s okay to reuse them? And if so, can you please put a license note on them, so they will be legally accessible?
Emanuele Feronato
Hello Igor,
use it as you want, there’s no restriction, ok?
Dylan
Thanks for this, it saved me A LOT of time.
I had to change Line 8 of de_1994 to $dh = sqrt(abs(($da*$da)+($db*$db)-($dc*$dc)));
Dylan
Change line 8 of de_1994 to:
$dh = sqrt(($da*$da)+($db*$db)+($dc*$dc));
Dylan
You are going to kill me!!! Ignore the nonsense I posted earlier…
Change line 8 of de_1994 to:
$dh = ($da * $da) + ($db * $db) – ($dc * $dc);
if ($dh < 0) { $dh = 0; } else { $dh = sqrt($dh); }
I guess the problem is an excessively large delta C resulting in a sqrt of a negative number. This was causing NaN on many comparisons.