From 8042dc2cd9df32cf33fa271e3c5261490c2fc187 Mon Sep 17 00:00:00 2001 From: blink1073 Date: Mon, 21 Jul 2014 19:48:27 -0500 Subject: [PATCH] Fix holes in lab conversions and add tests Fix holes in lab conversions and add tests Fix holes in lab conversions and add tests Add saturation protection and error raising by default Trigger travis rebuild after error Raise warning by default, change parameter name --- skimage/color/colorconv.py | 17 ++++++++++++++++- skimage/color/tests/test_colorconv.py | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 8e8c3b41..938bb849 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -53,6 +53,7 @@ References from __future__ import division +from warnings import warn import numpy as np from scipy import linalg from ..util import dtype @@ -552,6 +553,8 @@ def xyz2rgb(xyz): mask = arr > 0.0031308 arr[mask] = 1.055 * np.power(arr[mask], 1 / 2.4) - 0.055 arr[~mask] *= 12.92 + arr[arr < 0] = 0 + arr[arr > 1] = 1 return arr @@ -808,7 +811,7 @@ def xyz2lab(xyz, illuminant="D65", observer="2"): return np.concatenate([x[..., np.newaxis] for x in [L, a, b]], axis=-1) -def lab2xyz(lab, illuminant="D65", observer="2"): +def lab2xyz(lab, illuminant="D65", observer="2", error_on_invalid=False): """CIE-LAB to XYZcolor space conversion. Parameters @@ -819,6 +822,9 @@ def lab2xyz(lab, illuminant="D65", observer="2"): The name of the illuminant (the function is NOT case sensitive). observer : {"2", "10"}, optional The aperture angle of the observer. + error_on_invalid: optional, bool + If true, raise ValueError when pixels are outside the valid gamut. + Otherwise, raise a warning. Returns ------- @@ -854,6 +860,15 @@ def lab2xyz(lab, illuminant="D65", observer="2"): x = (a / 500.) + y z = y - (b / 200.) + if np.any(z < 0): + invalid = np.nonzero(z < 0) + msg = 'Color data out of range: Z < 0 in %s pixels' % invalid[0].size + if error_on_invalid: + raise ValueError(msg) + else: + warn(msg) + z[invalid] = 0 + out = np.dstack([x, y, z]) mask = out > 0.2068966 diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index 094873ae..2b435f67 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -11,6 +11,7 @@ Authors :license: modified BSD """ +from __future__ import division import os.path import numpy as np @@ -372,6 +373,29 @@ class TestColorconv(TestCase): img_rgb = img_as_float(self.img_rgb) assert_array_almost_equal(luv2rgb(rgb2luv(img_rgb)), img_rgb) + def test_lab_rgb_outlier(self): + lab_array = np.ones((3, 1, 3)) + lab_array[0] = [50, -12, 85] + lab_array[1] = [50, 12, -85] + lab_array[2] = [90, -4, -47] + rgb_array = np.array([[[0.501, 0.481, 0]], + [[0, 0.482, 1.]], + [[0.578, 0.914, 1.]], + ]) + assert_almost_equal(lab2rgb(lab_array), rgb_array, decimal=3) + + def test_lab_full_gamut(self): + a, b = np.meshgrid(np.arange(-100, 100), np.arange(-100, 100)) + L = np.ones(a.shape) + lab = np.dstack((L, a, b)) + assert_raises(ValueError, lambda: lab2xyz(lab)) + multipliers = [0, 10, 50, 100] + nan_percents = [12, 9, 0, 0] + for (l, perc) in zip(multipliers, nan_percents): + lab[:, :, 0] = l + out = lab2xyz(lab) + assert_equal(int(np.isnan(out).sum() / out.size * 100), perc) + def test_lab_lch_roundtrip(self): rgb = img_as_float(self.img_rgb) lab = rgb2lab(rgb)