diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 352e3039..ebc18bfb 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -45,7 +45,7 @@ from __future__ import division __all__ = ['convert_colorspace', 'rgb2hsv', 'hsv2rgb', 'rgb2xyz', 'xyz2rgb', 'rgb2rgbcie', 'rgbcie2rgb', 'rgb2grey', 'rgb2gray', 'gray2rgb', - 'xyz2lab', 'lab2xyz', + 'xyz2lab', 'lab2xyz', 'lab2rgb', 'rgb2lab' ] __docformat__ = "restructuredtext en" @@ -547,21 +547,6 @@ def gray2rgb(image): return np.dstack((image, image, image)) -#---------------------- -# Constants for CIE LAB -#---------------------- -_one_third = 1.0 / 3.0 -_sixteen_hundred_sixteenth = 16.0 / 116.0 -# Observer= 2A, Illuminant= D65 -_xref = 0.95047 -_yref = 1. -_zref = 1.08883 -_inv_xref = 1.0 / _xref -_inv_yref = 1.0 / _yref -_inv_zref = 1.0 / _zref - - - #-------------------------------------------------------------- # The conversion functions that make use of the constants above #-------------------------------------------------------------- @@ -588,50 +573,47 @@ def xyz2lab(xyz): ----- 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 -------- >>> import os - >>> from skimage import data_dir + >>> from skimage import data_dir >>> from skimage.color import rgb2xyz, xyz2lab >>> from skimage.io import imread >>> lena = imread(os.path.join(data_dir, 'lena.png')) >>> lena_xyz = rgb2xyz(lena) >>> lena_lab = xyz2lab(lena_xyz) """ - arr = _prepare_colorarray(xyz).copy() - out = np.empty_like(arr) + arr = _prepare_colorarray(xyz) + + #---------------------- + # Constants for CIE LAB + #---------------------- + # Observer= 2A, Illuminant= D65 + ref_white = np.array([0.95047, 1., 1.08883]) # scale by CIE XYZ tristimulus values of the reference white point - x, y, z = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2] - x *= _inv_xref - y *= _inv_yref - z *= _inv_zref + arr = arr / ref_white # Nonlinear distortion and linear transformation mask = arr > 0.008856 - arr[mask] = np.power(arr[mask], _one_third) - arr[~mask] = 7.787 * arr[~mask] + _sixteen_hundred_sixteenth - + 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) - # -- output - out[:, :, 0] = L - out[:, :, 1] = a - out[:, :, 2] = b + return np.dstack([L, a, b]) - # remove NaN - out[np.isnan(out)] = 0 - - return out def lab2xyz(lab): """CIE-LAB to XYZcolor space conversion. @@ -664,30 +646,69 @@ def lab2xyz(lab): """ arr = _prepare_colorarray(lab).copy() - out = np.empty_like(arr) L, a, b = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2] y = (L + 16.) / 116. x = (a / 500.) + y z = y - (b / 200.) - out[:, :, 0] = x - out[:, :, 1] = y - out[:, :, 2] = z + out = np.dstack([x, y, z]) mask = out > 0.2068966 out[mask] = np.power(out[mask], 3.) - out[~mask] = (out[~mask] - _sixteen_hundred_sixteenth) / 7.787*1000 + out[~mask] = (out[~mask] - 16.0 / 116.) / 7.787 # rescale Observer= 2 deg, Illuminant= D65 - #x, y, z = out[:, :, 0], out[:, :, 1], out[:, :, 2] - out[:, :, 0] *= _xref - out[:, :, 1] *= _yref - out[:, :, 2] *= _zref - - # remove NaN - out[np.isnan(out)] = 0 - + ref_white = np.array([0.95047, 1., 1.08883]) + out *= 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 7be4f454..316d801a 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -25,6 +25,7 @@ from skimage.color import ( convert_colorspace, rgb2grey, gray2rgb, xyz2lab, lab2xyz, + lab2rgb, rgb2lab ) from skimage import data_dir @@ -44,18 +45,17 @@ 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 + 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 + 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 @@ -167,10 +167,17 @@ class TestColorconv(TestCase): # 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) + 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_lab2xyz(self): - assert_array_almost_equal(lab2xyz(self.lab_array), self.xyz_array, decimal=3) def test_gray2rgb(): x = np.array([0, 0.5, 1])