From 80939997311a0a5837b017c7c194d1367f4d2d4d Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Tue, 22 Oct 2013 23:26:49 -0700 Subject: [PATCH 1/9] Luv colorspace conversions Adding XYZ <--> Luv, RGB <--> Adding test functions to mirror Lab tests --- skimage/color/__init__.py | 4 + skimage/color/colorconv.py | 191 ++++++++++++++++++++++++++ skimage/color/tests/test_colorconv.py | 42 ++++++ 3 files changed, 237 insertions(+) diff --git a/skimage/color/__init__.py b/skimage/color/__init__.py index 1c61020d..a471fd82 100644 --- a/skimage/color/__init__.py +++ b/skimage/color/__init__.py @@ -13,6 +13,10 @@ from .colorconv import (convert_colorspace, lab2xyz, lab2rgb, rgb2lab, + xyz2luv, + luv2xyz, + luv2rgb, + rgb2luv, rgb2hed, hed2rgb, lab2lch, diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 7562b650..cad4565e 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -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. diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index fbec9ba6..26b55e35 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -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) From 57eb69465c5d44bc2a51a1288429d5b76bfc09a9 Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Tue, 22 Oct 2013 23:29:51 -0700 Subject: [PATCH 2/9] Adding references regarding Lab and Luv spaces to docstring --- skimage/color/colorconv.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index cad4565e..97811bda 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -871,6 +871,12 @@ def xyz2luv(xyz): Notes ----- + According to "Principles of Color Technology" by Roy S. Berns, it is a myth + that "CIELAB was recommended for large color differences and that CIELUV was + recommended for small color differences." It is also a myth that "CIELAB is + recommended for reflecting samples while CIELUV is recommended for sources + and displays." + Observer= 2A, Illuminant= D65 CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883 From 36e56e26394a5598718a1e4b2f894c33894653d7 Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Tue, 22 Oct 2013 23:33:38 -0700 Subject: [PATCH 3/9] Removing unneeded division checking code --- skimage/color/colorconv.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 97811bda..bae63ac9 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -909,12 +909,6 @@ def xyz2luv(xyz): 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 ) @@ -976,12 +970,6 @@ def luv2xyz(luv): 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 ) From 9d9d16d2a49b22114857548f2d1478fc833647be Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Tue, 22 Oct 2013 23:52:12 -0700 Subject: [PATCH 4/9] renaming machineEps to eps to match convention --- skimage/color/colorconv.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index bae63ac9..17ebd2bc 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -898,7 +898,7 @@ def xyz2luv(xyz): # extract channels x, y, z = arr[..., 0], arr[..., 1], arr[..., 2] - machineEps = np.finfo(np.float).eps + eps = np.finfo(np.float).eps # compute y_r and L L = y / lab_ref_white[1] @@ -911,9 +911,9 @@ def xyz2luv(xyz): # u' and v' helper functions def fu( X, Y, Z ): - return ( 4.*X ) / ( X + 15.*Y + 3.*Z + machineEps ) + return ( 4.*X ) / ( X + 15.*Y + 3.*Z + eps ) def fv( X, Y, Z ): - return ( 9.*Y ) / ( X + 15.*Y + 3.*Z + machineEps ) + return ( 9.*Y ) / ( X + 15.*Y + 3.*Z + eps ) # compute u and v using helper functions u = 13.*L * ( fu(x,y,z) - u0 ) @@ -956,7 +956,7 @@ def luv2xyz(luv): L, u, v = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2] - machineEps = np.finfo(np.float).eps + eps = np.finfo(np.float).eps # compute y y = L.copy() @@ -971,8 +971,8 @@ def luv2xyz(luv): v0 = 9*lab_ref_white[1] / np.dot( uv_weights, lab_ref_white ) # compute intermediate values - a = u0 + u / ( 13.*L + machineEps ) - b = v0 + v / ( 13.*L + machineEps ) + a = u0 + u / ( 13.*L + eps ) + b = v0 + v / ( 13.*L + eps ) c = 3*y * (5*b-3) # compute x and z From df6a112bc51256a16c1d50b1dd6579faefdeebe8 Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Wed, 23 Oct 2013 00:07:00 -0700 Subject: [PATCH 5/9] flake8-compliant formatting --- skimage/color/colorconv.py | 45 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 17ebd2bc..c15ccf02 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -893,7 +893,7 @@ def xyz2luv(xyz): >>> lena_xyz = rgb2xyz(lena) >>> lena_luv = xyz2luv(lena_xyz) """ - arr = _prepare_colorarray(xyz) + arr = _prepare_colorarray(xyz) # extract channels x, y, z = arr[..., 0], arr[..., 1], arr[..., 2] @@ -901,25 +901,26 @@ def xyz2luv(xyz): eps = 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] + 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 ) + 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) # u' and v' helper functions - def fu( X, Y, Z ): - return ( 4.*X ) / ( X + 15.*Y + 3.*Z + eps ) - def fv( X, Y, Z ): - return ( 9.*Y ) / ( X + 15.*Y + 3.*Z + eps ) + def fu(X, Y, Z): + return (4.*X) / (X + 15.*Y + 3.*Z + eps) + + def fv(X, Y, Z): + return (9.*Y) / (X + 15.*Y + 3.*Z + eps) # compute u and v using helper functions - u = 13.*L * ( fu(x,y,z) - u0 ) - v = 13.*L * ( fv(x,y,z) - v0 ) + 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) + return np.concatenate([q[..., np.newaxis] for q in [L, u, v]], axis=-1) def luv2xyz(luv): @@ -961,25 +962,25 @@ def luv2xyz(luv): # compute y y = L.copy() mask = y > 7.999625 - y[mask] = np.power( (y[mask]+16.) / 116., 3.) + 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 ) + 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) # compute intermediate values - a = u0 + u / ( 13.*L + eps ) - b = v0 + v / ( 13.*L + eps ) + a = u0 + u / (13.*L + eps) + b = v0 + v / (13.*L + eps) 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 ) + 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) + return np.concatenate([q[..., np.newaxis] for q in [x, y, z]], axis=-1) def rgb2luv(rgb): From 44580817fcfdf1b59499fece18a390e0712692df Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Wed, 23 Oct 2013 12:42:40 -0700 Subject: [PATCH 6/9] Cleaning up docstrings --- skimage/color/colorconv.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index c15ccf02..e40e9c55 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -871,12 +871,6 @@ def xyz2luv(xyz): Notes ----- - According to "Principles of Color Technology" by Roy S. Berns, it is a myth - that "CIELAB was recommended for large color differences and that CIELUV was - recommended for small color differences." It is also a myth that "CIELAB is - recommended for reflecting samples while CIELUV is recommended for sources - and displays." - Observer= 2A, Illuminant= D65 CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883 @@ -924,7 +918,7 @@ def xyz2luv(xyz): def luv2xyz(luv): - """CIE-LAB to XYZcolor space conversion. + """CIE-Luv to XYZcolor space conversion. Parameters ---------- From c05d18a0107757815680d87ff699cf51a6620a77 Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Wed, 23 Oct 2013 22:21:15 -0700 Subject: [PATCH 7/9] docstring changes to correctly format input/output, clarifying XYZ and reference white --- skimage/color/colorconv.py | 60 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index e40e9c55..e7774003 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -854,25 +854,24 @@ def xyz2luv(xyz): Parameters ---------- - xyz : array_like - The image in XYZ format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + xyz : (M, N, [P,] 3) array_like + The 3 or 4 dimensional image in XYZ format. Final dimension denotes + channels. Returns ------- - out : ndarray - The image in CIE-Luv format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + out : (M, N, [P,] 3) ndarray + The image in CIE-Luv format. Same dimensions as input. Raises ------ ValueError - If `xyz` is not a 3-D array of shape (.., ..,[ ..,] 3). + If `xyz` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. Notes ----- - Observer= 2A, Illuminant= D65 - CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883 + XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 + Illuminant, with XYZ tristimulus values of `(95.047, 100., 108.883)`. References ---------- @@ -918,27 +917,28 @@ def xyz2luv(xyz): def luv2xyz(luv): - """CIE-Luv to XYZcolor space conversion. + """CIE-Luv to XYZ color space conversion. Parameters ---------- - lab : array_like - The image in lab format, in a 3-D array of shape (.., .., 3). + luv : (M, N, [P,] 3) array_like + The 3 or 4 dimensional image in CIE-Luv format. Final dimension denotes + channels. Returns ------- - out : ndarray - The image in XYZ format, in a 3-D array of shape (.., .., 3). + out : (M, N, [P,] 3) ndarray + The image in XYZ format. Same dimensions as input. Raises ------ ValueError - If `lab` is not a 3-D array of shape (.., .., 3). + If `luv` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. Notes ----- - Observer= 2A, Illuminant= D65 - CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883 + XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 + Illuminant, with XYZ tristimulus values of `(95.047, 100., 108.883)`. References ---------- @@ -978,24 +978,23 @@ def luv2xyz(luv): def rgb2luv(rgb): - """RGB to luv color space conversion. + """RGB to CIE-Luv color space conversion. Parameters ---------- - rgb : array_like - The image in RGB format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + rgb : (M, N, [P,] 3) array_like + The 3 or 4 dimensional image in RGB format. Final dimension denotes + channels. Returns ------- - out : ndarray - The image in Luv format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + out : (M, N, [P,] 3) ndarray + The image in CIE Luv format. Same dimensions as input. Raises ------ ValueError - If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3). + If `rgb` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. Notes ----- @@ -1009,18 +1008,19 @@ def luv2rgb(luv): Parameters ---------- - rgb : array_like - The image in Luv format, in a 3-D array of shape (.., .., 3). + luv : (M, N, [P,] 3) array_like + The 3 or 4 dimensional image in CIE Luv format. Final dimension denotes + channels. Returns ------- - out : ndarray - The image in RGB format, in a 3-D array of shape (.., .., 3). + out : (M, N, [P,] 3) ndarray + The image in RGB format. Same dimensions as input. Raises ------ ValueError - If `luv` is not a 3-D array of shape (.., .., 3). + If `luv` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. Notes ----- From 888ef05b63a89627a474e11afca894aa350a1536 Mon Sep 17 00:00:00 2001 From: Matthew Trentacoste Date: Wed, 23 Oct 2013 22:23:26 -0700 Subject: [PATCH 8/9] Adding note for Luv colorspace to top docstring --- skimage/color/colorconv.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index e7774003..39d35f56 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -29,6 +29,9 @@ Supported color spaces * LAB CIE : Lightness, a, b Colorspace derived from XYZ CIE that is intended to be more perceptually uniform +* LUV CIE : Lightness, u, v + Colorspace derived from XYZ CIE that is intended to be more + perceptually uniform * LCH CIE : Lightness, Chroma, Hue Defined in terms of LAB CIE. C and H are the polar representation of a and b. The polar angle C is defined to be on (0, 2*pi) From 3dd1bc17949c507a61413c2d69c415de862fe5a3 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sun, 27 Oct 2013 22:06:13 +0200 Subject: [PATCH 9/9] Format color conversion docstrings. --- skimage/color/colorconv.py | 146 +++++++++++++------------- skimage/color/tests/test_colorconv.py | 40 +++---- 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 39d35f56..e2c7af17 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -34,7 +34,7 @@ Supported color spaces perceptually uniform * LCH CIE : Lightness, Chroma, Hue Defined in terms of LAB CIE. C and H are the polar representation of - a and b. The polar angle C is defined to be on (0, 2*pi) + a and b. The polar angle C is defined to be on ``(0, 2*pi)`` :author: Nicolas Pinto (rgb2hsv) :author: Ralf Gommers (hsv2rgb) @@ -71,7 +71,7 @@ def guess_spatial_dimensions(image): ------- spatial_dims : int or None The number of spatial dimensions of `image`. If ambiguous, the value - is `None`. + is ``None``. Raises ------ @@ -125,12 +125,12 @@ def convert_colorspace(arr, fromspace, tospace): The image to convert. fromspace : str The color space to convert from. Valid color space strings are - ['RGB', 'HSV', 'RGB CIE', 'XYZ']. Value may also be specified as lower - case. + ``['RGB', 'HSV', 'RGB CIE', 'XYZ']``. Value may also be specified as + lower case. tospace : str The color space to convert to. Valid color space strings are - ['RGB', 'HSV', 'RGB CIE', 'XYZ']. Value may also be specified as lower - case. + ``['RGB', 'HSV', 'RGB CIE', 'XYZ']``. Value may also be specified as + lower case. Returns ------- @@ -140,7 +140,7 @@ def convert_colorspace(arr, fromspace, tospace): Notes ----- Conversion occurs through the "central" RGB color space, i.e. conversion - from XYZ to HSV is implemented as XYZ -> RGB -> HSV instead of directly. + from XYZ to HSV is implemented as ``XYZ -> RGB -> HSV`` instead of directly. Examples -------- @@ -184,17 +184,17 @@ def rgb2hsv(rgb): Parameters ---------- rgb : array_like - The image in RGB format, in a 3-D array of shape (.., .., 3). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Returns ------- out : ndarray - The image in HSV format, in a 3-D array of shape (.., .., 3). + The image in HSV format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `rgb` is not a 3-D array of shape (.., .., 3). + If `rgb` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- @@ -262,21 +262,21 @@ def hsv2rgb(hsv): Parameters ---------- hsv : array_like - The image in HSV format, in a 3-D array of shape (.., .., 3). + The image in HSV 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). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `hsv` is not a 3-D array of shape (.., .., 3). + If `hsv` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- - The conversion assumes an input data range of [0, 1] for all + The conversion assumes an input data range of ``[0, 1]`` for all color components. Conversion between RGB and HSV color spaces results in some loss of @@ -471,17 +471,17 @@ def xyz2rgb(xyz): Parameters ---------- xyz : array_like - The image in XYZ format, in a 3-D array of shape (.., .., 3). + The image in XYZ 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). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `xyz` is not a 3-D array of shape (.., .., 3). + If `xyz` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- @@ -516,18 +516,18 @@ def rgb2xyz(rgb): ---------- rgb : array_like The image in RGB format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Returns ------- out : ndarray The image in XYZ format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Raises ------ ValueError - If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3). + If `rgb` is not a 3- or 4-D array of shape ``(.., ..,[ ..,] 3)``. Notes ----- @@ -559,17 +559,17 @@ def rgb2rgbcie(rgb): Parameters ---------- rgb : array_like - The image in RGB format, in a 3-D array of shape (.., .., 3). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Returns ------- out : ndarray - The image in RGB CIE format, in a 3-D array of shape (.., .., 3). + The image in RGB CIE format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `rgb` is not a 3-D array of shape (.., .., 3). + If `rgb` is not a 3-D array of shape ``(.., .., 3)``. References ---------- @@ -591,17 +591,17 @@ def rgbcie2rgb(rgbcie): Parameters ---------- rgbcie : array_like - The image in RGB CIE format, in a 3-D array of shape (.., .., 3). + The image in RGB CIE 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). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `rgbcie` is not a 3-D array of shape (.., .., 3). + If `rgbcie` is not a 3-D array of shape ``(.., .., 3)``. References ---------- @@ -624,8 +624,8 @@ def rgb2gray(rgb): Parameters ---------- rgb : array_like - The image in RGB format, in a 3-D array of shape (.., .., 3), - or in RGBA format with shape (.., .., 4). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``, + or in RGBA format with shape ``(.., .., 4)``. Returns ------- @@ -635,8 +635,8 @@ def rgb2gray(rgb): Raises ------ ValueError - If `rgb2gray` is not a 3-D array of shape (.., .., 3) or - (.., .., 4). + If `rgb2gray` is not a 3-D array of shape ``(.., .., 3)`` or + ``(.., .., 4)``. References ---------- @@ -701,18 +701,18 @@ def xyz2lab(xyz): ---------- xyz : array_like The image in XYZ format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Returns ------- out : ndarray The image in CIE-LAB format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Raises ------ ValueError - If `xyz` is not a 3-D array of shape (.., ..,[ ..,] 3). + If `xyz` is not a 3-D array of shape ``(.., ..,[ ..,] 3)``. Notes ----- @@ -758,21 +758,21 @@ def lab2xyz(lab): Parameters ---------- lab : array_like - The image in lab format, in a 3-D array of shape (.., .., 3). + 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). + 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). + If `lab` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- - Observer= 2A, Illuminant= D65 + Observer = 2A, Illuminant = D65 CIE XYZ tristimulus values x_ref = 95.047, y_ref = 100., z_ref = 108.883 References @@ -807,18 +807,18 @@ def rgb2lab(rgb): ---------- rgb : array_like The image in RGB format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Returns ------- out : ndarray The image in Lab format, in a 3- or 4-D array of shape - (.., ..,[ ..,] 3). + ``(.., ..,[ ..,] 3)``. Raises ------ ValueError - If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3). + If `rgb` is not a 3- or 4-D array of shape ``(.., ..,[ ..,] 3)``. Notes ----- @@ -833,17 +833,17 @@ def lab2rgb(lab): Parameters ---------- rgb : array_like - The image in Lab format, in a 3-D array of shape (.., .., 3). + 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). + 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). + If `lab` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- @@ -858,7 +858,7 @@ def xyz2luv(xyz): Parameters ---------- xyz : (M, N, [P,] 3) array_like - The 3 or 4 dimensional image in XYZ format. Final dimension denotes + The 3 or 4 dimensional image in XYZ format. Final dimension denotes channels. Returns @@ -869,12 +869,12 @@ def xyz2luv(xyz): Raises ------ ValueError - If `xyz` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. + If `xyz` is not a 3-D or 4-D array of shape ``(M, N, [P,] 3)``. Notes ----- - XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 - Illuminant, with XYZ tristimulus values of `(95.047, 100., 108.883)`. + XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 + Illuminant, with XYZ tristimulus values of ``(95.047, 100., 108.883)``. References ---------- @@ -936,12 +936,12 @@ def luv2xyz(luv): Raises ------ ValueError - If `luv` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. + If `luv` is not a 3-D or 4-D array of shape ``(M, N, [P,] 3)``. Notes ----- - XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 - Illuminant, with XYZ tristimulus values of `(95.047, 100., 108.883)`. + XYZ conversion weights use Observer = 2A. Reference whitepoint for D65 + Illuminant, with XYZ tristimulus values of ``(95.047, 100., 108.883)``. References ---------- @@ -986,7 +986,7 @@ def rgb2luv(rgb): Parameters ---------- rgb : (M, N, [P,] 3) array_like - The 3 or 4 dimensional image in RGB format. Final dimension denotes + The 3 or 4 dimensional image in RGB format. Final dimension denotes channels. Returns @@ -997,7 +997,7 @@ def rgb2luv(rgb): Raises ------ ValueError - If `rgb` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. + If `rgb` is not a 3-D or 4-D array of shape ``(M, N, [P,] 3)``. Notes ----- @@ -1023,7 +1023,7 @@ def luv2rgb(luv): Raises ------ ValueError - If `luv` is not a 3-D or 4-D array of shape `(M, N, [P,] 3)`. + If `luv` is not a 3-D or 4-D array of shape ``(M, N, [P,] 3)``. Notes ----- @@ -1038,17 +1038,17 @@ def rgb2hed(rgb): Parameters ---------- rgb : array_like - The image in RGB format, in a 3-D array of shape (.., .., 3). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Returns ------- out : ndarray - The image in HED format, in a 3-D array of shape (.., .., 3). + The image in HED format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `rgb` is not a 3-D array of shape (.., .., 3). + If `rgb` is not a 3-D array of shape ``(.., .., 3)``. References @@ -1074,17 +1074,17 @@ def hed2rgb(hed): Parameters ---------- hed : array_like - The image in the HED color space, in a 3-D array of shape (.., .., 3). + The image in the HED color space, in a 3-D array of shape ``(.., .., 3)``. Returns ------- out : ndarray - The image in RGB, in a 3-D array of shape (.., .., 3). + The image in RGB, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `hed` is not a 3-D array of shape (.., .., 3). + If `hed` is not a 3-D array of shape ``(.., .., 3)``. References ---------- @@ -1110,19 +1110,19 @@ def separate_stains(rgb, conv_matrix): Parameters ---------- rgb : array_like - The image in RGB format, in a 3-D array of shape (.., .., 3). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. conv_matrix: ndarray The stain separation matrix as described by G. Landini [1]_. Returns ------- out : ndarray - The image in stain color space, in a 3-D array of shape (.., .., 3). + The image in stain color space, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `rgb` is not a 3-D array of shape (.., .., 3). + If `rgb` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- @@ -1164,19 +1164,19 @@ def combine_stains(stains, conv_matrix): Parameters ---------- stains : array_like - The image in stain color space, in a 3-D array of shape (.., .., 3). + The image in stain color space, in a 3-D array of shape ``(.., .., 3)``. conv_matrix: ndarray The stain separation matrix as described by G. Landini [1]_. Returns ------- out : ndarray - The image in RGB format, in a 3-D array of shape (.., .., 3). + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. Raises ------ ValueError - If `stains` is not a 3-D array of shape (.., .., 3). + If `stains` is not a 3-D array of shape ``(.., .., 3)``. Notes ----- @@ -1226,9 +1226,9 @@ def lab2lch(lab): Parameters ---------- lab : array_like - The N-D image in CIE-LAB format. The last (`N+1`th) dimension must have - at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` color - channels. Subsequent elements are copied. + The N-D image in CIE-LAB format. The last (``N+1``-th) dimension must + have at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` + color channels. Subsequent elements are copied. Returns ------- @@ -1242,7 +1242,7 @@ def lab2lch(lab): Notes ----- - The Hue is expressed as an angle between (0, 2*pi) + The Hue is expressed as an angle between ``(0, 2*pi)`` Examples -------- @@ -1262,7 +1262,7 @@ def lab2lch(lab): def _cart2polar_2pi(x, y): """convert cartesian coordiantes to polar (uses non-standard theta range!) - NON-STANDARD RANGE! Maps to (0, 2*pi) rather than usual (-pi, +pi) + NON-STANDARD RANGE! Maps to ``(0, 2*pi)`` rather than usual ``(-pi, +pi)`` """ r, t = np.hypot(x, y), np.arctan2(y, x) t += np.where(t < 0., 2 * np.pi, 0) @@ -1277,9 +1277,9 @@ def lch2lab(lch): Parameters ---------- lch : array_like - The N-D image in CIE-LCH format. The last (`N+1`th) dimension must have - at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` color - channels. Subsequent elements are copied. + The N-D image in CIE-LCH format. The last (``N+1``-th) dimension must + have at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` + color channels. Subsequent elements are copied. Returns ------- diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index 26b55e35..c7393355 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -71,24 +71,24 @@ class TestColorconv(TestCase): 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 - ]) + [[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 - ]) + [[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 + ]) 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 - ]) + [[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): @@ -259,7 +259,8 @@ 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 + # 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), @@ -277,16 +278,17 @@ class TestColorconv(TestCase): """ # Obtained with D65 white point, sRGB model and gamma gt_for_colbars = np.array([ - [100,0,0], + [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 + [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) + 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)