From efb651e48fd8fd163d00a20184370f323380544b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 May 2024 08:44:38 +0000 Subject: [PATCH] Implement support for "vK20" chromatic adaptation transform. --- README.rst | 4 +- colour/adaptation/__init__.py | 39 ++- colour/adaptation/fairchild2020.py | 321 +++++++++++++++++ colour/adaptation/tests/test_fairchild2020.py | 329 ++++++++++++++++++ docs/colour.adaptation.rst | 14 + docs/index.rst | 4 +- 6 files changed, 704 insertions(+), 7 deletions(-) create mode 100644 colour/adaptation/fairchild2020.py create mode 100644 colour/adaptation/tests/test_fairchild2020.py diff --git a/README.rst b/README.rst index 1300d7c0a6..29a8006fe9 100644 --- a/README.rst +++ b/README.rst @@ -322,13 +322,13 @@ Chromatic Adaptation - ``colour.adaptation`` array([ 0.2533053 , 0.13765138, 0.01543307]) + .. code-block:: python sorted(colour.CHROMATIC_ADAPTATION_METHODS) - .. code-block:: text - ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018'] + ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018', 'vK20'] Algebra - ``colour.algebra`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/colour/adaptation/__init__.py b/colour/adaptation/__init__.py index 1abb6e6ffb..05a13de33c 100644 --- a/colour/adaptation/__init__.py +++ b/colour/adaptation/__init__.py @@ -13,6 +13,9 @@ - :cite:`Fairchild2013t` : Fairchild, M. D. (2013). Chromatic Adaptation Models. In Color Appearance Models (3rd ed., pp. 4179-4252). Wiley. ISBN:B00DAYO8E2 +- :cite:`Fairchild2020` : Fairchild, M. D. (2020). Von Kries 2020: Evolution + of degree of chromatic adaptation. Color and Imaging Conference, 28(1), + 252-257. doi:10.2352/issn.2169-2629.2020.28.40 - :cite:`Li2002a` : Li, C., Luo, M. R., Rigg, B., & Hunt, R. W. G. (2002). CMC 2000 chromatic adaptation transform: CMCCAT2000. Color Research & Application, 27(1), 49-58. doi:10.1002/col.10005 @@ -54,6 +57,11 @@ chromatic_adaptation_VonKries, ) from .fairchild1990 import chromatic_adaptation_Fairchild1990 +from .fairchild2020 import ( + CONDITIONS_DEGREE_OF_ADAPTATION_VK20, + matrix_chromatic_adaptation_vk20, + chromatic_adaptation_vK20, +) from .cmccat2000 import ( InductionFactors_CMCCAT2000, VIEWING_CONDITIONS_CMCCAT2000, @@ -87,6 +95,11 @@ __all__ += [ "chromatic_adaptation_Fairchild1990", ] +__all__ += [ + "CONDITIONS_DEGREE_OF_ADAPTATION_VK20", + "matrix_chromatic_adaptation_vk20", + "chromatic_adaptation_vK20", +] __all__ += [ "InductionFactors_CMCCAT2000", "VIEWING_CONDITIONS_CMCCAT2000", @@ -108,6 +121,7 @@ "Fairchild 1990": chromatic_adaptation_Fairchild1990, "Von Kries": chromatic_adaptation_VonKries, "Zhai 2018": chromatic_adaptation_Zhai2018, + "vK20": chromatic_adaptation_vK20, } ) CHROMATIC_ADAPTATION_METHODS.__doc__ = """ @@ -116,8 +130,8 @@ References ---------- :cite:`CIETC1-321994b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s`, -:cite:`Fairchild2013t`, :cite:`Li2002a`, :cite:`Westland2012k`, -:cite:`Zhai2018` +:cite:`Fairchild2013t`, :cite:`Fairchild2020`, :cite:`Li2002a`, +:cite:`Westland2012k`, :cite:`Zhai2018` """ @@ -130,8 +144,9 @@ def chromatic_adaptation( "CIE 1994", "CMCCAT2000", "Fairchild 1990", - "Zhai 2018", "Von Kries", + "Zhai 2018", + "vK20", ] | str ) = "Von Kries", @@ -194,8 +209,16 @@ def chromatic_adaptation( {:func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Degree of adaptation :math:`D_\\Delta` of output illuminant :math:`\\Delta`. + XYZ_r + {:func:`colour.adaptation.chromatic_adaptation_vK20`}, + Reference viewing conditions *CIE XYZ* tristimulus values of + whitepoint. + coefficients + {:func:`colour.adaptation.chromatic_adaptation_vK20`}, + *vK20* degree of adaptation coefficients. transform {:func:`colour.adaptation.chromatic_adaptation_VonKries`, + :func:`colour.adaptation.chromatic_adaptation_vK20`, :func:`colour.adaptation.chromatic_adaptation_Zhai2018`}, Chromatic adaptation transform. XYZ_wo @@ -246,6 +269,14 @@ def chromatic_adaptation( ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) + *vK2020* chromatic adaptation: + + >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> chromatic_adaptation(XYZ, XYZ_w, XYZ_wr, method="vK20") + ... # doctest: +ELLIPSIS + array([ 0.2146884..., 0.1245616..., 0.0466255...]) + *CIE 1994* chromatic adaptation, requires extra *kwargs*: >>> XYZ = np.array([0.2800, 0.2126, 0.0527]) @@ -332,6 +363,8 @@ def chromatic_adaptation( kwargs.update({"XYZ_n": XYZ_w, "XYZ_r": XYZ_wr}) elif function is chromatic_adaptation_Zhai2018: kwargs.update({"XYZ_wb": XYZ_w, "XYZ_wd": XYZ_wr}) + elif function is chromatic_adaptation_vK20: + kwargs.update({"XYZ_p": XYZ_w, "XYZ_n": XYZ_wr}) XYZ_c = function(XYZ, **filter_kwargs(function, **kwargs)) diff --git a/colour/adaptation/fairchild2020.py b/colour/adaptation/fairchild2020.py new file mode 100644 index 0000000000..f0a97cd5e0 --- /dev/null +++ b/colour/adaptation/fairchild2020.py @@ -0,0 +1,321 @@ +""" +Von Kries 2020 (vK20) Chromatic Adaptation Model +================================================ + +Defines the *Von Kries 2020* (*vK20*) chromatic adaptation model objects: + +- :attr:`colour.adaptation.CONDITIONS_DEGREE_OF_ADAPTATION_VK20` +- :func:`colour.adaptation.matrix_chromatic_adaptation_vk20` +- :func:`colour.adaptation.chromatic_adaptation_vK20` + +References +---------- +- :cite:`Fairchild2020` : Fairchild, M. D. (2020). Von Kries 2020: Evolution + of degree of chromatic adaptation. Color and Imaging Conference, 28(1), + 252-257. doi:10.2352/issn.2169-2629.2020.28.40 +""" + +from __future__ import annotations + +from collections import namedtuple + +import numpy as np + +from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS +from colour.algebra import matrix_dot, sdiv, sdiv_mode, vector_dot +from colour.hints import ArrayLike, Literal, NDArrayFloat +from colour.utilities import ( + CanonicalMapping, + as_float_array, + from_range_1, + row_as_diagonal, + to_domain_1, + validate_method, +) + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" + +__all__ = [ + "Coefficients_DegreeOfAdaptation_vK20", + "CONDITIONS_DEGREE_OF_ADAPTATION_VK20", + "TVS_XYZ_R_VK20", + "matrix_chromatic_adaptation_vk20", + "chromatic_adaptation_vK20", +] + + +class Coefficients_DegreeOfAdaptation_vK20( + namedtuple("Coefficients_DegreeOfAdaptation_vK20", ("D_n", "D_r", "D_p")) +): + """ + *Von Kries 2020* (*vK20*) degree of adaptation coefficients. + + Parameters + ---------- + D_n + Degree of adaptation for the adapting illuminant. + D_r + Degree of adaptation for the reference illuminant. + D_p + Degree of adaptation for the previous illuminant. + + References + ---------- + :cite:`Fairchild2020` + """ + + +CONDITIONS_DEGREE_OF_ADAPTATION_VK20: CanonicalMapping = CanonicalMapping( + { + "Fairchild": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0), + "Hands": Coefficients_DegreeOfAdaptation_vK20(0.95, 0.05, 0), + "No Hands": Coefficients_DegreeOfAdaptation_vK20(0.85, 0.15, 0), + "Ordinal 1st": Coefficients_DegreeOfAdaptation_vK20(0.9, 0.1, 0), + "Ordinal 2nd": Coefficients_DegreeOfAdaptation_vK20(0.8, 0.1, 0.1), + "Reversibility Trial 1st": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0.1), + "Reversibility Trial 2nd": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.3, 0.1), + "Ma et al.": Coefficients_DegreeOfAdaptation_vK20(1 / 3, 1 / 3, 1 / 3), + "Hunt & Winter": Coefficients_DegreeOfAdaptation_vK20(0.6, 0.2, 0.2), + "Hurvich & Jameson": Coefficients_DegreeOfAdaptation_vK20(0.7, 0.3, 0), + "Simple von Kries": Coefficients_DegreeOfAdaptation_vK20(1, 0, 0), + } +) +CONDITIONS_DEGREE_OF_ADAPTATION_VK20.__doc__ = """ +Conditions for the *Von Kries 2020* (*vK20*) degree of adaptation coefficients. + +References +---------- +:cite:`Fairchild2020` +""" + +TVS_XYZ_R_VK20 = np.array([0.97941176, 1.00000000, 1.73235294]) +""" +*Von Kries 2020* (*vK20*) reference illuminant (taken to be +u' = 0.185, v' = 0.425, approximately 15000K, sky blue). + +References +---------- +:cite:`Fairchild2020` +""" + + +def matrix_chromatic_adaptation_vk20( + XYZ_p: ArrayLike, + XYZ_n: ArrayLike, + XYZ_r: ArrayLike = TVS_XYZ_R_VK20, + transform: Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ] + | str = "CAT02", + coefficients: Coefficients_DegreeOfAdaptation_vK20 = ( + CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"] + ), +) -> NDArrayFloat: + """ + Compute the *chromatic adaptation* matrix from previous viewing conditions + to adapting viewing conditions using *Von Kries 2020* (*vK20*) method. + + Parameters + ---------- + XYZ_p + Previous viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_n + Adapting viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_r + Reference viewing conditions *CIE XYZ* tristimulus values of + whitepoint. + transform + Chromatic adaptation transform. + coefficients + *vK20* degree of adaptation coefficients. + + Returns + ------- + :class:`numpy.ndarray` + Chromatic adaptation matrix :math:`M_{cat}`. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ_p`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_n`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Fairchild2020` + + Examples + -------- + >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n) + ... # doctest: +ELLIPSIS + array([[ 1.0279139...e+00, 2.9137117...e-02, -2.2794068...e-02], + [ 2.0702840...e-02, 9.9005316...e-01, -9.2143464...e-03], + [ -6.3758553...e-04, -1.1577319...e-03, 9.1296320...e-01]]) + + Using *Bradford* transform: + + >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> transform = "Bradford" + >>> matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n, transform=transform) + ... # doctest: +ELLIPSIS + array([[ 1.0367230..., 0.0195580..., -0.0219321...], + [ 0.0276321..., 0.9822296..., -0.0082419...], + [-0.0029508..., 0.0040690..., 0.9102430...]]) + """ + + XYZ_n = as_float_array(XYZ_n) + XYZ_r = as_float_array(XYZ_r) + XYZ_p = as_float_array(XYZ_p) + + transform = validate_method( + transform, + tuple(CHROMATIC_ADAPTATION_TRANSFORMS), + '"{0}" chromatic adaptation transform is invalid, it must be one of {1}!', + ) + + M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] + + D_n, D_r, D_p = coefficients + + LMS_n = vector_dot(M, XYZ_n) + LMS_r = vector_dot(M, XYZ_r) + LMS_p = vector_dot(M, XYZ_p) + + with sdiv_mode(): + D = row_as_diagonal(sdiv(1, (D_n * LMS_n + D_r * LMS_r + D_p * LMS_p))) + + M_CAT = matrix_dot(np.linalg.inv(M), D) + M_CAT = matrix_dot(M_CAT, M) + + return M_CAT + + +def chromatic_adaptation_vK20( + XYZ: ArrayLike, + XYZ_p: ArrayLike, + XYZ_n: ArrayLike, + XYZ_r: ArrayLike = TVS_XYZ_R_VK20, + transform: Literal[ + "Bianco 2010", + "Bianco PC 2010", + "Bradford", + "CAT02 Brill 2008", + "CAT02", + "CAT16", + "CMCCAT2000", + "CMCCAT97", + "Fairchild", + "Sharp", + "Von Kries", + "XYZ Scaling", + ] + | str = "CAT02", + coefficients: Coefficients_DegreeOfAdaptation_vK20 = ( + CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Fairchild"] + ), +) -> NDArrayFloat: + """ + Adapt given stimulus from previous viewing conditions to adapting viewing + conditions using *Von Kries 2020* (*vK20*) method. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values of stimulus to adapt. + XYZ_p + Previous viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_n + Adapting viewing conditions *CIE XYZ* tristimulus values of whitepoint. + XYZ_r + Reference viewing conditions *CIE XYZ* tristimulus values of + whitepoint. + transform + Chromatic adaptation transform. + coefficients + *vK20* degree of adaptation coefficients. + + Returns + ------- + :class:`numpy.ndarray` + *CIE XYZ_c* tristimulus values of the stimulus corresponding colour. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_p`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_n`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + | ``XYZ_r`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + +------------+-----------------------+---------------+ + | **Range** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ_a`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + References + ---------- + :cite:`Fairchild2020` + + Examples + -------- + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n) + ... # doctest: +ELLIPSIS + array([ 0.2146884..., 0.1245616..., 0.0466255...]) + + Using *Bradford* transform: + + >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + >>> XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + >>> XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + >>> transform = "Bradford" + >>> chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n, transform=transform) + ... # doctest: +ELLIPSIS + array([ 0.2153837..., 0.1250885..., 0.0466455...]) + """ + + XYZ = to_domain_1(XYZ) + XYZ_p = to_domain_1(XYZ_p) + XYZ_n = to_domain_1(XYZ_n) + XYZ_r = to_domain_1(XYZ_r) + + M_CAT = matrix_chromatic_adaptation_vk20( + XYZ_p, XYZ_n, XYZ_r, transform, coefficients + ) + XYZ_a = vector_dot(M_CAT, XYZ) + + return from_range_1(XYZ_a) diff --git a/colour/adaptation/tests/test_fairchild2020.py b/colour/adaptation/tests/test_fairchild2020.py new file mode 100644 index 0000000000..9827062427 --- /dev/null +++ b/colour/adaptation/tests/test_fairchild2020.py @@ -0,0 +1,329 @@ +""" +Define the unit tests for the :mod:`colour.adaptation.fairchild2020` module. +""" + +import unittest +from itertools import product + +import numpy as np + +from colour.adaptation import ( + chromatic_adaptation_vK20, + matrix_chromatic_adaptation_vk20, +) +from colour.adaptation.fairchild2020 import ( + CONDITIONS_DEGREE_OF_ADAPTATION_VK20, +) +from colour.constants import TOLERANCE_ABSOLUTE_TESTS +from colour.utilities import domain_range_scale, ignore_numpy_errors + +__author__ = "Colour Developers" +__copyright__ = "Copyright 2013 Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" + +__all__ = [ + "TestMatrixChromaticAdaptationVonKries", + "TestChromaticAdaptationVonKries", +] + + +class TestMatrixChromaticAdaptationVonKries(unittest.TestCase): + """ + Define :func:`colour.adaptation.fairchild2020.\ +matrix_chromatic_adaptation_vk20` definition unit tests methods. + """ + + def test_matrix_chromatic_adaptation_vk20(self): + """ + Test :func:`colour.adaptation.fairchild2020.\ +matrix_chromatic_adaptation_vk20` definition. + """ + + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + ), + np.array( + [ + [1.02791390, 0.02913712, -0.02279407], + [0.02070284, 0.99005317, -0.00921435], + [-0.00063759, -0.00115773, 0.91296320], + ] + ), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([1.09846607, 1.00000000, 0.35582280]), + ), + np.array( + [ + [0.94760338, -0.05816939, 0.06647414], + [-0.04151006, 1.02361127, 0.02667016], + [0.00163074, 0.00391656, 1.29341031], + ] + ), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="XYZ Scaling", + ), + np.array( + [ + [1.03217229, 0.00000000, 0.00000000], + [0.00000000, 1.00000000, 0.00000000], + [0.00000000, 0.00000000, 0.91134516], + ] + ), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="Bradford", + ), + np.array( + [ + [1.03672305, 0.01955802, -0.02193210], + [0.02763218, 0.98222961, -0.00824197], + [-0.00295083, 0.00406903, 0.91024305], + ] + ), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="Von Kries", + coefficients=CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Simple Von Kries"], + ), + np.array( + [ + [0.98446157, -0.05474538, 0.06773143], + [-0.00601339, 1.00479590, 0.00121235], + [0.00000000, 0.00000000, 1.31990977], + ] + ), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_n_dimensional_matrix_chromatic_adaptation_vk20(self): + """ + Test :func:`colour.adaptation.fairchild2020.\ +matrix_chromatic_adaptation_vk20` definition n-dimensional arrays support. + """ + + XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + M = matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n) + + XYZ_p = np.tile(XYZ_p, (6, 1)) + XYZ_n = np.tile(XYZ_n, (6, 1)) + M = np.reshape(np.tile(M, (6, 1)), (6, 3, 3)) + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n), + M, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + XYZ_p = np.reshape(XYZ_p, (2, 3, 3)) + XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) + M = np.reshape(M, (2, 3, 3, 3)) + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n), + M, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_domain_range_scale_matrix_chromatic_adaptation_vk20(self): + """ + Test :func:`colour.adaptation.fairchild2020.\ +matrix_chromatic_adaptation_vk20` definition domain and range scale + support. + """ + + XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + XYZ_r = np.array([0.97941176, 1.00000000, 1.73235294]) + M = matrix_chromatic_adaptation_vk20(XYZ_p, XYZ_n) + + d_r = (("reference", 1), ("1", 1), ("100", 1)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_array_almost_equal( + matrix_chromatic_adaptation_vk20( + XYZ_p * factor, XYZ_n * factor, XYZ_r * factor + ), + M, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + @ignore_numpy_errors + def test_nan_matrix_chromatic_adaptation_vk20(self): + """ + Test :func:`colour.adaptation.fairchild2020.\ +matrix_chromatic_adaptation_vk20` 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)))) + matrix_chromatic_adaptation_vk20(cases, cases) + + +class TestChromaticAdaptationVonKries(unittest.TestCase): + """ + Define :func:`colour.adaptation.fairchild2020.chromatic_adaptation_vK20` + definition unit tests methods. + """ + + def test_chromatic_adaptation_vK20(self): + """ + Test :func:`colour.adaptation.fairchild2020.chromatic_adaptation_vK20` + definition. + """ + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + ), + np.array([0.21468842, 0.12456164, 0.04662558]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.14222010, 0.23042768, 0.10495772]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([1.09846607, 1.00000000, 0.35582280]), + ), + np.array([0.12834138, 0.23276404, 0.13688781]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.07818780, 0.06157201, 0.28099326]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.99144661, 1.00000000, 0.67315942]), + ), + np.array([0.07908008, 0.06167829, 0.28354175]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="XYZ Scaling", + ), + np.array([0.21318495, 0.12197225, 0.04681536]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="Bradford", + ), + np.array([0.21538376, 0.12508852, 0.04664559]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + np.array([0.20654008, 0.12197225, 0.05136952]), + np.array([0.95045593, 1.00000000, 1.08905775]), + np.array([0.96429568, 1.00000000, 0.82510460]), + transform="Von Kries", + coefficients=CONDITIONS_DEGREE_OF_ADAPTATION_VK20["Simple Von Kries"], + ), + np.array([0.20013269, 0.12137749, 0.06780313]), + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_n_dimensional_chromatic_adaptation_vK20(self): + """ + Test :func:`colour.adaptation.fairchild2020.chromatic_adaptation_vK20` + definition n-dimensional arrays support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + XYZ_a = chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n) + + XYZ = np.tile(XYZ, (6, 1)) + XYZ_p = np.tile(XYZ_p, (6, 1)) + XYZ_n = np.tile(XYZ_n, (6, 1)) + XYZ_a = np.tile(XYZ_a, (6, 1)) + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n), + XYZ_a, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + XYZ = np.reshape(XYZ, (2, 3, 3)) + XYZ_p = np.reshape(XYZ_p, (2, 3, 3)) + XYZ_n = np.reshape(XYZ_n, (2, 3, 3)) + XYZ_a = np.reshape(XYZ_a, (2, 3, 3)) + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n), + XYZ_a, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + def test_domain_range_scale_chromatic_adaptation_vK20(self): + """ + Test :func:`colour.adaptation.fairchild2020.chromatic_adaptation_vK20` + definition domain and range scale support. + """ + + XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) + XYZ_p = np.array([0.95045593, 1.00000000, 1.08905775]) + XYZ_n = np.array([0.96429568, 1.00000000, 0.82510460]) + XYZ_r = np.array([0.97941176, 1.00000000, 1.73235294]) + XYZ_a = chromatic_adaptation_vK20(XYZ, XYZ_p, XYZ_n) + + d_r = (("reference", 1), ("1", 1), ("100", 100)) + for scale, factor in d_r: + with domain_range_scale(scale): + np.testing.assert_array_almost_equal( + chromatic_adaptation_vK20( + XYZ * factor, + XYZ_p * factor, + XYZ_n * factor, + XYZ_r * factor, + ), + XYZ_a * factor, + decimal=TOLERANCE_ABSOLUTE_TESTS, + ) + + @ignore_numpy_errors + def test_nan_chromatic_adaptation_vK20(self): + """ + Test :func:`colour.adaptation.fairchild2020.chromatic_adaptation_vK20` + 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)))) + chromatic_adaptation_vK20(cases, cases, cases) diff --git a/docs/colour.adaptation.rst b/docs/colour.adaptation.rst index 03a7b093d8..7f73a5a05a 100644 --- a/docs/colour.adaptation.rst +++ b/docs/colour.adaptation.rst @@ -36,6 +36,20 @@ Fairchild (1990) chromatic_adaptation_Fairchild1990 +Fairchild (2020) +---------------- + +``colour.adaptation`` + +.. currentmodule:: colour.adaptation + +.. autosummary:: + :toctree: generated/ + + CONDITIONS_DEGREE_OF_ADAPTATION_VK20 + matrix_chromatic_adaptation_vk20 + chromatic_adaptation_vK20 + CIE 1994 -------- diff --git a/docs/index.rst b/docs/index.rst index fe0b31287f..9a81fed694 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -97,13 +97,13 @@ Chromatic Adaptation - ``colour.adaptation`` array([ 0.2533053 , 0.13765138, 0.01543307]) -.. code-block:: python +.. code-block:: python sorted(colour.CHROMATIC_ADAPTATION_METHODS) .. code-block:: text + ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018', 'vK20'] - ['CIE 1994', 'CMCCAT2000', 'Fairchild 1990', 'Von Kries', 'Zhai 2018'] Algebra - ``colour.algebra`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~