diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 5a158a4c..ec5551d6 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -44,7 +44,9 @@ References from __future__ import division __all__ = ['convert_colorspace', 'rgb2hsv', 'hsv2rgb', 'rgb2xyz', 'xyz2rgb', - 'rgb2rgbcie', 'rgbcie2rgb', 'rgb2grey', 'rgb2gray', 'gray2rgb'] + 'rgb2rgbcie', 'rgbcie2rgb', 'rgb2grey', 'rgb2gray', 'gray2rgb', + 'xyz2lab', 'lab2xyz', 'lab2rgb', 'rgb2lab' + ] __docformat__ = "restructuredtext en" @@ -81,11 +83,8 @@ def convert_colorspace(arr, fromspace, tospace): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> from skimage import data + >>> lena = data.lena() >>> lena_hsv = convert_colorspace(lena, 'RGB', 'HSV') """ fromdict = {'RGB': lambda im: im, 'HSV': hsv2rgb, 'RGB CIE': rgbcie2rgb, @@ -149,11 +148,9 @@ def rgb2hsv(rgb): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> from skimage import color + >>> from skimage import data + >>> lena = data.lena() >>> lena_hsv = color.rgb2hsv(lena) """ arr = _prepare_colorarray(rgb) @@ -229,11 +226,8 @@ def hsv2rgb(hsv): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> from skimage import data + >>> lena = data.lena() >>> lena_hsv = rgb2hsv(lena) >>> lena_rgb = hsv2rgb(lena_hsv) """ @@ -291,6 +285,9 @@ grey_from_rgb = np.array([[0.2125, 0.7154, 0.0721], [0, 0, 0], [0, 0, 0]]) +# CIE LAB constants for Observer= 2A, Illuminant= D65 +lab_ref_white = np.array([0.95047, 1., 1.08883]) + #------------------------------------------------------------- # The conversion functions that make use of the matrices above #------------------------------------------------------------- @@ -351,14 +348,11 @@ def xyz2rgb(xyz): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread + >>> from skimage import data >>> from skimage.color import rgb2xyz, xyz2rgb - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> lena = data.lena() >>> lena_xyz = rgb2xyz(lena) - >>> lena_rgb = xyz2rgb(lena_hsv) + >>> lena_rgb = xyz2rgb(lena_xyz) """ return _convert(rgb_from_xyz, xyz) @@ -392,11 +386,8 @@ def rgb2xyz(rgb): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> from skimage import data + >>> lena = data.lena() >>> lena_xyz = rgb2xyz(lena) """ return _convert(xyz_from_rgb, rgb) @@ -426,12 +417,9 @@ def rgb2rgbcie(rgb): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread + >>> from skimage import data >>> from skimage.color import rgb2rgbcie - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> lena = data.lena() >>> lena_rgbcie = rgb2rgbcie(lena) """ return _convert(rgbcie_from_rgb, rgb) @@ -461,14 +449,11 @@ def rgbcie2rgb(rgbcie): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread + >>> from skimage import data >>> from skimage.color import rgb2rgbcie, rgbcie2rgb - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> lena = data.lena() >>> lena_rgbcie = rgb2rgbcie(lena) - >>> lena_rgb = rgbcie2rgb(lena_hsv) + >>> lena_rgb = rgbcie2rgb(lena_rgbcie) """ return _convert(rgb_from_rgbcie, rgbcie) @@ -508,12 +493,9 @@ def rgb2grey(rgb): Examples -------- - >>> import os - >>> from skimage import data_dir - >>> from skimage.io import imread >>> from skimage.color import rgb2grey - - >>> lena = imread(os.path.join(data_dir, 'lena.png')) + >>> from skimage import data + >>> lena = data.lena() >>> lena_grey = rgb2grey(lena) """ if rgb.ndim == 2: @@ -548,3 +530,157 @@ def gray2rgb(image): M, N = image.shape return np.dstack((image, image, image)) + + +def xyz2lab(xyz): + """XYZ to CIE-LAB color space conversion. + + Parameters + ---------- + xyz : array_like + The image in XYZ format, in a 3-D array of shape (.., .., 3). + + Returns + ------- + out : ndarray + The image in CIE-LAB format, in a 3-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=07#text7 + .. [2] http://en.wikipedia.org/wiki/Lab_color_space + + Examples + -------- + >>> from skimage import data + >>> from skimage.color import rgb2xyz, xyz2lab + >>> lena = data.lena() + >>> lena_xyz = rgb2xyz(lena) + >>> lena_lab = xyz2lab(lena_xyz) + """ + arr = _prepare_colorarray(xyz) + + # scale by CIE XYZ tristimulus values of the reference white point + arr = arr / lab_ref_white + + # Nonlinear distortion and linear transformation + mask = arr > 0.008856 + arr[mask] = np.power(arr[mask], 1. / 3.) + arr[~mask] = 7.787 * arr[~mask] + 16. / 116. + + x, y, z = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2] + + # Vector scaling + L = (116. * y) - 16. + a = 500.0 * (x - y) + b = 200.0 * (y - z) + + return np.dstack([L, a, b]) + + +def lab2xyz(lab): + """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=07#text7 + .. [2] http://en.wikipedia.org/wiki/Lab_color_space + + """ + + arr = _prepare_colorarray(lab).copy() + + L, a, b = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2] + y = (L + 16.) / 116. + x = (a / 500.) + y + z = y - (b / 200.) + + out = np.dstack([x, y, z]) + + mask = out > 0.2068966 + out[mask] = np.power(out[mask], 3.) + out[~mask] = (out[~mask] - 16.0 / 116.) / 7.787 + + # rescale Observer= 2 deg, Illuminant= D65 + out *= lab_ref_white + return out + + +def rgb2lab(rgb): + """RGB to lab color space conversion. + + Parameters + ---------- + rgb : array_like + The image in RGB format, in a 3-D array of shape (.., .., 3). + + Returns + ------- + out : ndarray + The image in Lab format, in a 3-D array of shape (.., .., 3). + + Raises + ------ + ValueError + If `rgb` is not a 3-D array of shape (.., .., 3). + + Notes + ----- + This function uses rgb2xyz and xyz2lab. + """ + return xyz2lab(rgb2xyz(rgb)) + + +def lab2rgb(lab): + """Lab to RGB color space conversion. + + Parameters + ---------- + rgb : array_like + The image in Lab 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 `lab` is not a 3-D array of shape (.., .., 3). + + Notes + ----- + This function uses lab2xyz and xyz2rgb. + """ + return xyz2rgb(lab2xyz(lab)) diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index cfe81c58..316d801a 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -23,7 +23,9 @@ from skimage.color import ( rgb2xyz, xyz2rgb, rgb2rgbcie, rgbcie2rgb, convert_colorspace, - rgb2grey, gray2rgb + rgb2grey, gray2rgb, + xyz2lab, lab2xyz, + lab2rgb, rgb2lab ) from skimage import data_dir @@ -43,6 +45,19 @@ class TestColorconv(TestCase): colbars_point75 = colbars * 0.75 colbars_point75_array = np.swapaxes(colbars_point75.reshape(3, 4, 2), 0, 2) + xyz_array = np.array([[[0.4124, 0.21260, 0.01930]], # red + [[0, 0, 0]], # black + [[.9505, 1., 1.089]], # white + [[.1805, .0722, .9505]], # blue + [[.07719, .15438, .02573]], # green + ]) + lab_array = np.array([[[53.233, 80.109, 67.220]], # red + [[0., 0., 0.]], # black + [[100.0, 0.005, -0.010]], # white + [[32.303, 79.197, -107.864]], # blue + [[46.229, -51.7, 49.898]], # green + ]) + # RGB to HSV def test_rgb2hsv_conversion(self): rgb = img_as_float(self.img_rgb)[::16, ::16] @@ -149,6 +164,20 @@ class TestColorconv(TestCase): def test_rgb2grey_on_grey(self): rgb2grey(np.random.random((5, 5))) + # test matrices for xyz2lab and lab2xyz generated using http://www.easyrgb.com/index.php?X=CALC + # Note: easyrgb website displays xyz*100 + def test_xyz2lab(self): + assert_array_almost_equal(xyz2lab(self.xyz_array), + self.lab_array, decimal=3) + + def test_lab2xyz(self): + assert_array_almost_equal(lab2xyz(self.lab_array), + self.xyz_array, decimal=3) + + def test_lab_rgb_roundtrip(self): + img_rgb = img_as_float(self.img_rgb) + assert_array_almost_equal(lab2rgb(rgb2lab(img_rgb)), img_rgb) + def test_gray2rgb(): x = np.array([0, 0.5, 1])