Here's code to convert from rgb to Lab and back again.
I am using this to convert an RGB rep of an HTML color to it's Lab equivalent, raising or lowering the Luminance part and then converting back again to an RGB value and thence to an HTML color.
I couldn't find anything about cube roots, hence the _cuberoot and _cube
# I do not do the linear approximation for $u/$un <= 0.008859
sub RGB2Lab {
my ($red, $green, $blue) = @_;
my ($x, $y, $z) = _RGB2XYZitu($red, $green, $blue);
my ($xn, $yn, $zn) = _RGB2XYZitu(1,1,1);
my $fx = _cuberoot($x/$xn);
my $fy = _cuberoot($y/$yn);
my $fz = _cuberoot($z/$zn);
return ( 116*$fy-16, 500*($fx - $fy), 200*($fy - $fz) );
}
sub Lab2RGB {
my ($L, $a, $b) = @_;
my ($xn, $yn, $zn) = _RGB2XYZitu(1,1,1);
my $fL = _cube(($L+16)/116);
my $fa = _cube($a/500);
my $fb = _cube($b/200);
my $y = _cube( ($L+16)/116) * $yn;
my $x = _cube( ($L+16)/116 + $a/500 ) * $xn;
my $z = _cube( ($L+16)/116 - $b/200 ) * $zn;
return _XYZitu2RGB($x, $y, $z);
}
sub _RGB2XYZitu {
my ($r, $g, $b) = @_;
return (
0.431*$r + 0.342*$g + 0.178*$b,
0.222*$r + 0.707*$g + 0.071*$b,
0.020*$r + 0.130*$g + 0.939*$b
);
}
sub _XYZitu2RGB {
my ($x, $y, $z) = @_;
return map { $_ > 1 ? 1 : $_ } (
3.063*$x - 1.393*$y - 0.476*$z,
-0.969*$x + 1.876*$y + 0.042*$z,
0.068*$x - 0.229*$y + 1.069*$z
);
}
sub _cuberoot {
my $x = shift;
return 0 if $x == 0;
my $sign = ($x < 0) ? -1 : 1;
$x *= $sign;
return $sign * exp( log($x)/3.0 );
}
sub _cube {
my $x = shift;
return 0 if $x == 0;
my $sign = ($x < 0) ? -1 : 1;
$x *= $sign;
return $sign * exp( 3 * log($x) );
}
|