Luv colorspace conversions

Adding XYZ <--> Luv, RGB <-->
Adding test functions to mirror Lab tests
This commit is contained in:
Matthew Trentacoste
2013-10-22 23:26:49 -07:00
parent 36cfd6d17c
commit 8093999731
3 changed files with 237 additions and 0 deletions
+4
View File
@@ -13,6 +13,10 @@ from .colorconv import (convert_colorspace,
lab2xyz,
lab2rgb,
rgb2lab,
xyz2luv,
luv2xyz,
luv2rgb,
rgb2luv,
rgb2hed,
hed2rgb,
lab2lch,
+191
View File
@@ -849,6 +849,197 @@ def lab2rgb(lab):
return xyz2rgb(lab2xyz(lab))
def xyz2luv(xyz):
"""XYZ to CIE-Luv color space conversion.
Parameters
----------
xyz : array_like
The image in XYZ format, in a 3- or 4-D array of shape
(.., ..,[ ..,] 3).
Returns
-------
out : ndarray
The image in CIE-Luv format, in a 3- or 4-D array of shape
(.., ..,[ ..,] 3).
Raises
------
ValueError
If `xyz` is not a 3-D array of shape (.., ..,[ ..,] 3).
Notes
-----
Observer= 2A, Illuminant= D65
CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=16#text16
.. [2] http://en.wikipedia.org/wiki/CIELUV
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2xyz, xyz2luv
>>> lena = data.lena()
>>> lena_xyz = rgb2xyz(lena)
>>> lena_luv = xyz2luv(lena_xyz)
"""
arr = _prepare_colorarray(xyz)
# extract channels
x, y, z = arr[..., 0], arr[..., 1], arr[..., 2]
machineEps = np.finfo(np.float).eps
# compute y_r and L
L = y / lab_ref_white[1]
mask = L > 0.008856
L[mask] = 116. * np.power( L[mask], 1. / 3. ) - 16.
L[~mask] = 903.3 * L[~mask]
u0 = 4*lab_ref_white[0] / np.dot( [1, 15, 3], lab_ref_white )
v0 = 9*lab_ref_white[1] / np.dot( [1, 15, 3], lab_ref_white )
def div_nan_check( n, d ):
out = n / d
mask = np.isnan( out )
out[mask] = 0.
return out
# u' and v' helper functions
def fu( X, Y, Z ):
return ( 4.*X ) / ( X + 15.*Y + 3.*Z + machineEps )
def fv( X, Y, Z ):
return ( 9.*Y ) / ( X + 15.*Y + 3.*Z + machineEps )
# compute u and v using helper functions
u = 13.*L * ( fu(x,y,z) - u0 )
v = 13.*L * ( fv(x,y,z) - v0 )
return np.concatenate([x[..., np.newaxis] for x in [L, u, v]], axis=-1)
def luv2xyz(luv):
"""CIE-LAB to XYZcolor space conversion.
Parameters
----------
lab : array_like
The image in lab format, in a 3-D array of shape (.., .., 3).
Returns
-------
out : ndarray
The image in XYZ format, in a 3-D array of shape (.., .., 3).
Raises
------
ValueError
If `lab` is not a 3-D array of shape (.., .., 3).
Notes
-----
Observer= 2A, Illuminant= D65
CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=16#text16
.. [2] http://en.wikipedia.org/wiki/CIELUV
"""
arr = _prepare_colorarray(luv).copy()
L, u, v = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2]
machineEps = np.finfo(np.float).eps
# compute y
y = L.copy()
mask = y > 7.999625
y[mask] = np.power( (y[mask]+16.) / 116., 3.)
y[~mask] = y[~mask] / 903.3
y *= lab_ref_white[1]
# reference white x,z
uv_weights = [1, 15, 3]
u0 = 4*lab_ref_white[0] / np.dot( uv_weights, lab_ref_white )
v0 = 9*lab_ref_white[1] / np.dot( uv_weights, lab_ref_white )
def div_nan_check( n, d ):
out = n / d
mask = np.isnan( out )
out[mask] = 0.
return out
# compute intermediate values
a = u0 + u / ( 13.*L + machineEps )
b = v0 + v / ( 13.*L + machineEps )
c = 3*y * (5*b-3)
# compute x and z
z = ( (a-4)*c - 15*a*b*y ) / ( 12*b )
x = -( c/b + 3.*z )
return np.concatenate([x[..., np.newaxis] for x in [x, y, z]], axis=-1)
def rgb2luv(rgb):
"""RGB to luv color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
(.., ..,[ ..,] 3).
Returns
-------
out : ndarray
The image in Luv format, in a 3- or 4-D array of shape
(.., ..,[ ..,] 3).
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3).
Notes
-----
This function uses rgb2xyz and xyz2luv.
"""
return xyz2luv(rgb2xyz(rgb))
def luv2rgb(luv):
"""Luv to RGB color space conversion.
Parameters
----------
rgb : array_like
The image in Luv format, in a 3-D array of shape (.., .., 3).
Returns
-------
out : ndarray
The image in RGB format, in a 3-D array of shape (.., .., 3).
Raises
------
ValueError
If `luv` is not a 3-D array of shape (.., .., 3).
Notes
-----
This function uses luv2xyz and xyz2rgb.
"""
return xyz2rgb(luv2xyz(luv))
def rgb2hed(rgb):
"""RGB to Haematoxylin-Eosin-DAB (HED) color space conversion.
+42
View File
@@ -33,6 +33,8 @@ from skimage.color import (rgb2hsv, hsv2rgb,
rgb2grey, gray2rgb,
xyz2lab, lab2xyz,
lab2rgb, rgb2lab,
xyz2luv, luv2xyz,
luv2rgb, rgb2luv,
is_rgb, is_gray,
lab2lch, lch2lab,
guess_spatial_dimensions
@@ -81,6 +83,13 @@ class TestColorconv(TestCase):
[[46.229, -51.7, 49.898]], # green
])
luv_array = np.array([[[53.233, 175.053, 37.751]], # red
[[0., 0., 0.]], # black
[[100., 0.001, -0.017]], # white
[[32.303, -9.400, -130.358]], # blue
[[46.228, -43.774, 56.589]], # green
])
# RGB to HSV
def test_rgb2hsv_conversion(self):
rgb = img_as_float(self.img_rgb)[::16, ::16]
@@ -250,6 +259,39 @@ class TestColorconv(TestCase):
img_rgb = img_as_float(self.img_rgb)
assert_array_almost_equal(lab2rgb(rgb2lab(img_rgb)), img_rgb)
# test matrices for xyz2luv and luv2xyz generated using http://www.easyrgb.com/index.php?X=CALC
# Note: easyrgb website displays xyz*100
def test_xyz2luv(self):
assert_array_almost_equal(xyz2luv(self.xyz_array),
self.luv_array, decimal=3)
def test_luv2xyz(self):
assert_array_almost_equal(luv2xyz(self.luv_array),
self.xyz_array, decimal=3)
def test_rgb2luv_brucelindbloom(self):
"""
Test the RGB->Lab conversion by comparing to the calculator on the
authoritative Bruce Lindbloom
[website](http://brucelindbloom.com/index.html?ColorCalculator.html).
"""
# Obtained with D65 white point, sRGB model and gamma
gt_for_colbars = np.array([
[100,0,0],
[97.1393, 7.7056, 106.7866],
[91.1132, -70.4773, -15.2042],
[87.7347, -83.0776, 107.3985],
[60.3242, 84.0714, -108.6834],
[53.2408, 175.0151, 37.7564],
[32.2970, -9.4054, -130.3423],
[0,0,0]]).T
gt_array = np.swapaxes(gt_for_colbars.reshape(3, 4, 2), 0, 2)
assert_array_almost_equal(rgb2luv(self.colbars_array), gt_array, decimal=2)
def test_luv_rgb_roundtrip(self):
img_rgb = img_as_float(self.img_rgb)
assert_array_almost_equal(luv2rgb(rgb2luv(img_rgb)), img_rgb)
def test_lab_lch_roundtrip(self):
rgb = img_as_float(self.img_rgb)
lab = rgb2lab(rgb)