Coverage for appearance/hke.py: 42%
31 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
1"""
2Helmholtz—Kohlrausch Effect
3===========================
5Define methods for estimating the Helmholtz—Kohlrausch effect (HKE).
7- :attr:`colour.HKE_NAYATANI1997_METHODS`: *Nayatani (1997)* HKE
8 computation methods, choice between variable achromatic colour (VAC)
9 and variable chromatic colour (VCC).
10- :func:`colour.HelmholtzKohlrausch_effect_object_Nayatani1997`:
11 Compute HKE value for object colours using *Nayatani (1997)* method.
12- :func:`colour.HelmholtzKohlrausch_effect_luminous_Nayatani1997`:
13 Compute HKE value for luminous colours using *Nayatani (1997)* method.
14- :func:`colour.appearance.coefficient_q_Nayatani1997`:
15 Calculate :math:`q` coefficient for *Nayatani (1997)* HKE estimation.
16- :func:`colour.appearance.coefficient_K_Br_Nayatani1997`:
17 Calculate :math:`K_{Br}` coefficient for *Nayatani (1997)* HKE
18 estimation.
20References
21----------
22- :cite:`Nayatani1997` : Nayatani, Y. (1997). Simple estimation methods for
23 the Helmholtz—Kohlrausch effect. Color Research & Application, 22(6),
24 385-401. doi:10.1002/(SICI)1520-6378(199712)22:6<385::AID-COL6>3.0.CO;2-R
25"""
27from __future__ import annotations
29import typing
31import numpy as np
33from colour.algebra import spow
35if typing.TYPE_CHECKING:
36 from colour.hints import ArrayLike, DTypeFloat, Literal, NDArray, NDArrayFloat
38from colour.utilities import CanonicalMapping, as_float_array, tsplit, validate_method
40__author__ = "Ilia Sibiryakov"
41__copyright__ = "Copyright 2013 Colour Developers"
42__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
43__maintainer__ = "Colour Developers"
44__email__ = "colour-developers@colour-science.org"
45__status__ = "Production"
47__all__ = [
48 "HKE_NAYATANI1997_METHODS",
49 "HelmholtzKohlrausch_effect_object_Nayatani1997",
50 "HelmholtzKohlrausch_effect_luminous_Nayatani1997",
51 "coefficient_q_Nayatani1997",
52 "coefficient_K_Br_Nayatani1997",
53]
55HKE_NAYATANI1997_METHODS = CanonicalMapping(
56 {
57 "VAC": -0.1340,
58 "VCC": -0.8660,
59 }
60)
61HKE_NAYATANI1997_METHODS.__doc__ = """
62Define *Nayatani (1997)* *Helmholtz-Kohlrausch effect* (HKE) computation
63methods: variable achromatic colour ('VAC') and variable chromatic
64colour ('VCC').
66References
67----------
68:cite:`Nayatani1997`
69"""
72def HelmholtzKohlrausch_effect_object_Nayatani1997(
73 uv: ArrayLike,
74 uv_c: ArrayLike,
75 L_a: ArrayLike,
76 method: Literal["VAC", "VCC"] | str = "VCC",
77) -> NDArrayFloat:
78 """
79 Compute the *Helmholtz-Kohlrausch effect* (HKE) value for object
80 colours using the *Nayatani (1997)* method.
82 Parameters
83 ----------
84 uv
85 *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates
86 of the test samples.
87 uv_c
88 *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates
89 of the reference white.
90 L_a
91 Adapting luminance in :math:`cd/m^2`.
92 method
93 Estimation method to use: *VCC* (Variable-Chromatic-Colour) or
94 *VAC* (Variable-Achromatic-Colour).
96 Returns
97 -------
98 :class:`numpy.ndarray`
99 Luminance factor (:math:`\\Gamma`) values computed using the
100 *Nayatani (1997)* object colour estimation method.
102 References
103 ----------
104 :cite:`Nayatani1997`
106 Examples
107 --------
108 >>> import colour
109 >>> white = colour.xy_to_Luv_uv(colour.temperature.CCT_to_xy_CIE_D(6504))
110 >>> colours = colour.XYZ_to_xy(
111 ... [colour.wavelength_to_XYZ(430 + i * 50) for i in range(5)]
112 ... )
113 >>> L_adapting = 65
114 >>> HelmholtzKohlrausch_effect_object_Nayatani1997( # doctest: +ELLIPSIS
115 ... colour.xy_to_Luv_uv(colours), white, L_adapting
116 ... )
117 array([ 2.2468383..., 1.4619799..., 1.1801658..., 0.9031318..., \
1181.7999376...])
119 """
121 u, v = tsplit(uv)
122 u_c, v_c = tsplit(uv_c)
124 method = validate_method(method, tuple(HKE_NAYATANI1997_METHODS))
126 K_Br = coefficient_K_Br_Nayatani1997(L_a)
127 q = coefficient_q_Nayatani1997(np.arctan2(v - v_c, u - u_c))
128 S_uv = 13 * np.sqrt((u - u_c) ** 2 + (v - v_c) ** 2)
130 return 1 + (HKE_NAYATANI1997_METHODS[method] * q + 0.0872 * K_Br) * S_uv
133def HelmholtzKohlrausch_effect_luminous_Nayatani1997(
134 uv: ArrayLike,
135 uv_c: ArrayLike,
136 L_a: ArrayLike,
137 method: Literal["VAC", "VCC"] | str = "VCC",
138) -> NDArrayFloat:
139 """
140 Compute the *HKE* factor for luminous colours using the
141 *Nayatani (1997)* method.
143 Parameters
144 ----------
145 uv
146 *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates of
147 test samples.
148 uv_c
149 *CIE L\\*u\\*v\\** colourspace :math:`uv^p` chromaticity coordinates of
150 reference white.
151 L_a
152 Adapting luminance in :math:`cd/m^2`.
153 method
154 Estimation method to use: *VCC* (Variable-Chromatic-Colour) or
155 *VAC* (Variable-Achromatic-Colour).
157 Returns
158 -------
159 :class:`numpy.ndarray`
160 Luminance factor (:math:`\\Gamma`) values computed using the
161 Nayatani luminous colour estimation method.
163 References
164 ----------
165 :cite:`Nayatani1997`
167 Examples
168 --------
169 >>> import colour
170 >>> white = colour.xy_to_Luv_uv(colour.temperature.CCT_to_xy_CIE_D(6504))
171 >>> colours = colour.XYZ_to_xy(
172 ... [colour.wavelength_to_XYZ(430 + i * 50) for i in range(5)]
173 ... )
174 >>> L_adapting = 65
175 >>> HelmholtzKohlrausch_effect_luminous_Nayatani1997( # doctest: +ELLIPSIS
176 ... colour.xy_to_Luv_uv(colours), white, L_adapting
177 ... )
178 array([ 7.4460471..., 2.4767159..., 1.4723422..., 0.7938695..., \
1794.1828629...])
180 """
182 return (
183 0.4462
184 * (
185 HelmholtzKohlrausch_effect_object_Nayatani1997(uv, uv_c, L_a, method)
186 + 0.3086
187 )
188 ** 3
189 )
192def coefficient_q_Nayatani1997(
193 theta: ArrayLike,
194) -> NDArrayFloat:
195 """
196 Compute the :math:`q(\\theta)` coefficient for *Nayatani (1997)* *HKE*
197 computations.
199 The hue angle :math:`\\theta` can be computed as follows:
201 :math:`tan^{-1}\\cfrac{v' - v'_c}{u' - u'_c}`
203 where :math:`u'` and :math:`v'` are the *CIE L\\*u\\*v\\** colourspace
204 :math:`uv^p` chromaticity coordinates of the test chromatic light and
205 :math:`u'_c` and :math:`v'_c` are the *CIE L\\*u\\*v\\**
206 colourspace :math:`uv^p` chromaticity coordinates of the reference
207 white light.
209 Parameters
210 ----------
211 theta
212 Hue angle (:math:`\\theta`) in radians.
214 Returns
215 -------
216 :class:`numpy.ndarray`
217 :math:`q` coefficient for *Nayatani (1997)* *HKE* methods.
219 References
220 ----------
221 :cite:`Nayatani1997`
223 Examples
224 --------
225 This recreates *FIG. A-1*.
227 >>> import matplotlib.pyplot as plt
228 >>> angles = [(np.pi * 2 / 100 * i) for i in range(100)]
229 >>> q_values = coefficient_q_Nayatani1997(angles)
230 >>> plt.plot(np.array(angles), q_values / (np.pi * 2) * 180)
231 ... # doctest: +ELLIPSIS
232 [<matplotlib.lines.Line2D object at 0x...>]
233 >>> plt.show() # doctest: +SKIP
234 """
236 theta = as_float_array(theta)
238 theta_2, theta_3, theta_4 = 2 * theta, 3 * theta, 4 * theta
240 return (
241 -0.01585
242 - 0.03017 * np.cos(theta)
243 - 0.04556 * np.cos(theta_2)
244 - 0.02667 * np.cos(theta_3)
245 - 0.00295 * np.cos(theta_4)
246 + 0.14592 * np.sin(theta)
247 + 0.05084 * np.sin(theta_2)
248 - 0.01900 * np.sin(theta_3)
249 - 0.00764 * np.sin(theta_4)
250 )
253@typing.overload
254def coefficient_K_Br_Nayatani1997(L_a: float | DTypeFloat) -> DTypeFloat: ...
255@typing.overload
256def coefficient_K_Br_Nayatani1997(L_a: NDArray) -> NDArrayFloat: ...
257@typing.overload
258def coefficient_K_Br_Nayatani1997(L_a: ArrayLike) -> DTypeFloat | NDArrayFloat: ...
259def coefficient_K_Br_Nayatani1997(L_a: ArrayLike) -> DTypeFloat | NDArrayFloat:
260 """
261 Compute the :math:`K_{Br}` coefficient for *Nayatani (1997)* *HKE*
262 computations.
264 Parameters
265 ----------
266 L_a
267 Adapting luminance in :math:`cd/m^2`.
269 Returns
270 -------
271 :class:`numpy.ndarray`
272 :math:`K_{Br}` coefficient for *Nayatani (1997)* *HKE* methods.
274 Notes
275 -----
276 - The :math:`K_{Br}` coefficient is normalised to unity around
277 :math:`63.66cd/m^2`.
279 References
280 ----------
281 :cite:`Nayatani1997`
283 Examples
284 --------
285 >>> L_a_values = [10 + i * 20 for i in range(5)]
286 >>> coefficient_K_Br_Nayatani1997(L_a_values) # doctest: +ELLIPSIS
287 array([ 0.7134481..., 0.8781172..., 0.9606248..., 1.0156689..., \
2881.0567008...])
289 >>> coefficient_K_Br_Nayatani1997(63.66) # doctest: +ELLIPSIS
290 1.0001284...
291 """
293 L_a_4495 = spow(L_a, 0.4495)
295 return (L_a_4495 * 6.362 + 6.469) * 0.2717 / (L_a_4495 + 6.469)