From b34542a95eeab873f8b3441ba5f003c9cc1ffe9a Mon Sep 17 00:00:00 2001 From: mesvam Date: Sat, 9 Nov 2024 23:15:31 -0500 Subject: [PATCH 1/3] add HyCH color difference metric add HyCH tests --- colour/difference/__init__.py | 7 ++ colour/difference/delta_e.py | 67 +++++++++++++++++ colour/difference/tests/test_delta_e.py | 97 +++++++++++++++++++++++++ colour/hints/__init__.py | 1 + docs/colour.difference.rst | 12 +++ 5 files changed, 184 insertions(+) diff --git a/colour/difference/__init__.py b/colour/difference/__init__.py index d909bc0dc..013362b2d 100644 --- a/colour/difference/__init__.py +++ b/colour/difference/__init__.py @@ -54,6 +54,7 @@ delta_E_CMC, delta_E_ITP, delta_E_HyAB, + delta_E_HyCH, ) from .din99 import delta_E_DIN99 from .huang2015 import power_function_Huang2015 @@ -77,6 +78,7 @@ "delta_E_CMC", "delta_E_ITP", "delta_E_HyAB", + "delta_E_HyCH", ] __all__ += [ "delta_E_DIN99", @@ -105,6 +107,7 @@ "CAM16-UCS": delta_E_CAM16UCS, "DIN99": delta_E_DIN99, "HyAB": delta_E_HyAB, + "HyCH": delta_E_HyCH, } ) DELTA_E_METHODS.__doc__ = """ @@ -210,6 +213,10 @@ def delta_E( >>> b = np.array([53.12207516, -39.92365056, 249.54831278]) >>> delta_E(a, b, method="HyAB") # doctest: +ELLIPSIS 151.0215481... + >>> a = np.array([39.91531343, 51.16658481, 146.12933781]) + >>> b = np.array([53.12207516, -39.92365056, 249.54831278]) + >>> delta_E(a, b, method="HyCH") # doctest: +ELLIPSIS + 48.66427941... """ method = validate_method(method, tuple(DELTA_E_METHODS)) diff --git a/colour/difference/delta_e.py b/colour/difference/delta_e.py index 925723779..b3085786f 100644 --- a/colour/difference/delta_e.py +++ b/colour/difference/delta_e.py @@ -13,6 +13,7 @@ - :func:`colour.difference.delta_E_CMC` - :func:`colour.difference.delta_E_ITP` - :func:`colour.difference.delta_E_HyAB` +- :func:`colour.difference.delta_E_HyCH` References ---------- @@ -79,6 +80,7 @@ "delta_E_CMC", "delta_E_ITP", "delta_E_HyAB", + "delta_E_HyCH", ] JND_CIE1976 = 2.3 @@ -712,3 +714,68 @@ def delta_E_HyAB(Lab_1: ArrayLike, Lab_2: ArrayLike) -> NDArrayFloat: HyAB = np.abs(dL) + np.hypot(da, db) return as_float(HyAB) + + +def delta_E_HyCH( + Lab_1: ArrayLike, Lab_2: ArrayLike, textiles: bool = False +) -> NDArrayFloat: + """ + Return the difference between two *CIE L\\*a\\*b\\** colourspace arrays + This metric is intended for large color differences, + on the order of 10 CIELAB units or greater + + Parameters + ---------- + Lab_1 + *CIE L\\*a\\*b\\** colourspace array 1. + Lab_2 + *CIE L\\*a\\*b\\** colourspace array 2. + + Returns + ------- + :class:`numpy.ndarray` + Colour difference HyCH. + + Notes + ----- + +------------+-----------------------+-------------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===================+ + | ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] | + | | | | + | | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] | + | | | | + | | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] | + +------------+-----------------------+-------------------+ + | ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] | + | | | | + | | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] | + | | | | + | | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] | + +------------+-----------------------+-------------------+ + + References + ---------- + :cite:`Abasi2020` + + Examples + -------- + >>> Lab_1 = np.array([39.91531343, 51.16658481, 146.12933781]) + >>> Lab_2 = np.array([53.12207516, -39.92365056, 249.54831278]) + >>> delta_E_HyCH(Lab_1, Lab_2) # doctest: +ELLIPSIS + 48.664279419760369... + """ + + S_L, S_C, S_H, delta_L_p, delta_C_p, delta_H_p, R_T = astuple( + intermediate_attributes_CIE2000(Lab_1, Lab_2) + ) + + k_L = 2 if textiles else 1 + k_C = 1 + k_H = 1 + + HyCH = np.abs(delta_L_p / (k_L * S_L)) + np.sqrt( + (delta_C_p / (k_C * S_C)) ** 2 + (delta_H_p / (k_H * S_H)) ** 2 + ) + + return as_float(HyCH) diff --git a/colour/difference/tests/test_delta_e.py b/colour/difference/tests/test_delta_e.py index 945a2bbf7..e282b33d6 100644 --- a/colour/difference/tests/test_delta_e.py +++ b/colour/difference/tests/test_delta_e.py @@ -21,6 +21,7 @@ delta_E_CIE2000, delta_E_CMC, delta_E_HyAB, + delta_E_HyCH, delta_E_ITP, ) from colour.difference.delta_e import intermediate_attributes_CIE2000 @@ -40,6 +41,7 @@ "TestDelta_E_CMC", "TestDelta_E_ITP", "TestDelta_E_HyAB", + "TestDelta_E_HyCH", ] @@ -918,3 +920,98 @@ def test_nan_delta_E_HyAB(self): cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = np.array(list(set(product(cases, repeat=3)))) delta_E_HyAB(cases, cases) + + +class TestDelta_E_HyCH: + """ + Define :func:`colour.difference.delta_e.delta_E_HyCH` definition unit + tests methods. + """ + + def test_delta_E_HyCH(self): + """Test :func:`colour.difference.delta_e.delta_E_HyCH` definition.""" + + np.testing.assert_allclose( + delta_E_HyCH( + np.array([39.91531343, 51.16658481, 146.12933781]), + np.array([53.12207516, -39.92365056, 249.54831278]), + ), + 48.664279419760369, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_allclose( + delta_E_HyCH( + np.array([39.91531343, 51.16658481, 146.12933781]), + np.array([28.52234779, 19.46628874, 472.06042624]), + ), + 39.260928157999118, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_allclose( + delta_E_HyCH( + np.array([48.99183622, -0.10561667, 400.65619925]), + np.array([50.65907324, -0.11671910, 402.82235718]), + ), + 1.7806293290163562, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_n_dimensional_delta_E_HyCH(self): + """ + Test :func:`colour.difference.delta_e.delta_E_HyCH` definition + n-dimensional arrays support. + """ + + Lab_1 = (np.array([39.91531343, 51.16658481, 146.12933781]),) + Lab_2 = (np.array([53.12207516, -39.92365056, 249.54831278]),) + delta_E = delta_E_HyCH(Lab_1, Lab_2) + + Lab_1 = np.tile(Lab_1, (6, 1)) + Lab_2 = np.tile(Lab_2, (6, 1)) + delta_E = np.tile(delta_E, 6) + np.testing.assert_allclose( + delta_E_HyCH(Lab_1, Lab_2), + delta_E, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + Lab_1 = np.reshape(Lab_1, (2, 3, 3)) + Lab_2 = np.reshape(Lab_2, (2, 3, 3)) + delta_E = np.reshape(delta_E, (2, 3)) + np.testing.assert_allclose( + delta_E_HyCH(Lab_1, Lab_2), + delta_E, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_domain_range_scale_delta_E_HyCH(self): + """ + Test :func:`colour.difference.delta_e.delta_E_HyCH` definition + domain and range scale support. + """ + + Lab_1 = np.array([39.91531343, 51.16658481, 146.12933781]) + Lab_2 = np.array([53.12207516, -39.92365056, 249.54831278]) + delta_E = delta_E_HyCH(Lab_1, Lab_2) + + d_r = (("reference", 1), ("1", 0.01), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_allclose( + delta_E_HyCH(Lab_1 * factor, Lab_2 * factor), + delta_E, + atol=TOLERANCE_ABSOLUTE_TESTS, + ) + + @ignore_numpy_errors + def test_nan_delta_E_HyCH(self): + """ + Test :func:`colour.difference.delta_e.delta_E_HyCH` definition nan + support. + """ + + cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] + cases = np.array(list(set(product(cases, repeat=3)))) + delta_E_HyCH(cases, cases) diff --git a/colour/hints/__init__.py b/colour/hints/__init__.py index ad923ccea..60e913f46 100644 --- a/colour/hints/__init__.py +++ b/colour/hints/__init__.py @@ -589,6 +589,7 @@ def apply(self, RGB: ArrayLike, **kwargs: Any) -> NDArray: # noqa: D102 "CMC", "DIN99", "HyAB", + "HyCH", "ITP", "cie1976", "cie1994", diff --git a/docs/colour.difference.rst b/docs/colour.difference.rst index e639aacc9..d88b6f42c 100644 --- a/docs/colour.difference.rst +++ b/docs/colour.difference.rst @@ -127,6 +127,18 @@ HyAB delta_E_HyAB +HyCH +----- + +``colour.difference`` + +.. currentmodule:: colour.difference + +.. autosummary:: + :toctree: generated/ + + delta_E_HyCH + Standardized Residual Sum of Squares (STRESS) Index --------------------------------------------------- From 49bd0f746e8ee8066362f53031d49b419b47590c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Mon, 11 Nov 2024 21:45:31 +1300 Subject: [PATCH 2/3] Update various docstrings. --- colour/difference/delta_e.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/colour/difference/delta_e.py b/colour/difference/delta_e.py index b3085786f..77c7aa61b 100644 --- a/colour/difference/delta_e.py +++ b/colour/difference/delta_e.py @@ -721,8 +721,13 @@ def delta_E_HyCH( ) -> NDArrayFloat: """ Return the difference between two *CIE L\\*a\\*b\\** colourspace arrays - This metric is intended for large color differences, - on the order of 10 CIELAB units or greater + using a combination of a Euclidean metric in hue and chroma with a + city-block metric to incorporate lightness differences based on + *CIE 2000* recommendation attributes. + + This metric is intended for large colour differences, on the order of 10 + CIE L\\*a\\*b\\** units or greater. + Parameters ---------- @@ -756,7 +761,7 @@ def delta_E_HyCH( References ---------- - :cite:`Abasi2020` + :cite:`Abasi2020a` Examples -------- From 96f6b7952a1899379f2062ed84f83a4664d986a9 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Mon, 11 Nov 2024 21:45:57 +1300 Subject: [PATCH 3/3] Update documentation files. --- docs/colour.difference.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/colour.difference.rst b/docs/colour.difference.rst index d88b6f42c..a3cce4731 100644 --- a/docs/colour.difference.rst +++ b/docs/colour.difference.rst @@ -116,7 +116,7 @@ DIN99 delta_E_DIN99 HyAB ------ +---- ``colour.difference`` @@ -128,7 +128,7 @@ HyAB delta_E_HyAB HyCH ------ +---- ``colour.difference``