Coverage for colour/colorimetry/whiteness.py: 100%
61 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""
2Whiteness Index :math:`W`
3=========================
5Define *whiteness* index :math:`W` computation methods.
7- :func:`colour.colorimetry.whiteness_Berger1959`: Compute *whiteness*
8 index :math:`WI` from the specified sample *CIE XYZ* tristimulus values
9 using *Berger (1959)* method.
10- :func:`colour.colorimetry.whiteness_Taube1960`: Compute *whiteness*
11 index :math:`WI` from the specified sample *CIE XYZ* tristimulus values
12 using *Taube (1960)* method.
13- :func:`colour.colorimetry.whiteness_Stensby1968`: Compute *whiteness*
14 index :math:`WI` from the specified sample *CIE L\\*a\\*b\\** colourspace
15 array using *Stensby (1968)* method.
16- :func:`colour.colorimetry.whiteness_ASTME313`: Compute *whiteness*
17 index :math:`WI` from the specified sample *CIE XYZ* tristimulus values
18 using *ASTM E313* method.
19- :func:`colour.colorimetry.whiteness_Ganz1979`: Compute *whiteness*
20 index :math:`W` and *tint* :math:`T` from the specified sample *CIE xy*
21 chromaticity coordinates using *Ganz and Griesser (1979)* method.
22- :func:`colour.colorimetry.whiteness_CIE2004`: Compute *whiteness*
23 :math:`W` or :math:`W_{10}` and *tint* :math:`T` or :math:`T_{10}`
24 from the specified sample *CIE xy* chromaticity coordinates using
25 *CIE 2004* method.
26- :attr:`colour.WHITENESS_METHODS`: Supported *whiteness* computation
27 methods.
28- :func:`colour.whiteness`: Compute *whiteness* :math:`W` using
29 specified method.
31References
32----------
33- :cite:`CIETC1-482004k` : CIE TC 1-48. (2004). The evaluation of whiteness.
34 In CIE 015:2004 Colorimetry, 3rd Edition (p. 24). ISBN:978-3-901906-33-6
35- :cite:`Wikipedia2004b` : Wikipedia. (2004). Whiteness. Retrieved September
36 17, 2014, from http://en.wikipedia.org/wiki/Whiteness
37- :cite:`Wyszecki2000ba` : Wyszecki, Günther, & Stiles, W. S. (2000). Table
38 I(6.5.3) Whiteness Formulae (Whiteness Measure Denoted by W). In Color
39 Science: Concepts and Methods, Quantitative Data and Formulae (pp.
40 837-839). Wiley. ISBN:978-0-471-39918-6
41- :cite:`X-Rite2012a` : X-Rite, & Pantone. (2012). Color iQC and Color iMatch
42 Color Calculations Guide.
43 https://www.xrite.com/-/media/xrite/files/apps_engineering_techdocuments/\
44c/09_color_calculations_en.pdf
45"""
47from __future__ import annotations
49import typing
51if typing.TYPE_CHECKING:
52 from colour.hints import Any, Literal
54from colour.hints import ( # noqa: TC001
55 ArrayLike,
56 Domain100,
57 Range100,
58)
59from colour.utilities import (
60 CanonicalMapping,
61 as_float,
62 as_float_array,
63 filter_kwargs,
64 from_range_100,
65 get_domain_range_scale,
66 to_domain_100,
67 tsplit,
68 tstack,
69 validate_method,
70)
72__author__ = "Colour Developers"
73__copyright__ = "Copyright 2013 Colour Developers"
74__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
75__maintainer__ = "Colour Developers"
76__email__ = "colour-developers@colour-science.org"
77__status__ = "Production"
79__all__ = [
80 "whiteness_Berger1959",
81 "whiteness_Taube1960",
82 "whiteness_Stensby1968",
83 "whiteness_ASTME313",
84 "whiteness_Ganz1979",
85 "whiteness_CIE2004",
86 "WHITENESS_METHODS",
87 "whiteness",
88]
91def whiteness_Berger1959(XYZ: Domain100, XYZ_0: Domain100) -> Range100:
92 """
93 Compute the *whiteness* index :math:`WI` of the specified sample *CIE XYZ*
94 tristimulus values using the *Berger (1959)* method.
96 Parameters
97 ----------
98 XYZ
99 *CIE XYZ* tristimulus values of the sample.
100 XYZ_0
101 *CIE XYZ* tristimulus values of the reference white.
103 Returns
104 -------
105 :class:`numpy.ndarray`
106 *Whiteness* index :math:`WI`.
108 Notes
109 -----
110 +------------+-----------------------+---------------+
111 | **Domain** | **Scale - Reference** | **Scale - 1** |
112 +============+=======================+===============+
113 | ``XYZ`` | 100 | 1 |
114 +------------+-----------------------+---------------+
115 | ``XYZ_0`` | 100 | 1 |
116 +------------+-----------------------+---------------+
118 +------------+-----------------------+---------------+
119 | **Range** | **Scale - Reference** | **Scale - 1** |
120 +============+=======================+===============+
121 | ``WI`` | 100 | 1 |
122 +------------+-----------------------+---------------+
124 - *Whiteness* :math:`WI` values larger than 33.33 indicate a bluish
125 white and values smaller than 33.33 indicate a yellowish white.
127 References
128 ----------
129 :cite:`X-Rite2012a`
131 Examples
132 --------
133 >>> import numpy as np
134 >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000])
135 >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595])
136 >>> whiteness_Berger1959(XYZ, XYZ_0) # doctest: +ELLIPSIS
137 30.3638017...
138 """
140 X, Y, Z = tsplit(to_domain_100(XYZ))
141 X_0, _Y_0, Z_0 = tsplit(to_domain_100(XYZ_0))
143 WI = 0.333 * Y + 125 * (Z / Z_0) - 125 * (X / X_0)
145 return as_float(from_range_100(WI))
148def whiteness_Taube1960(XYZ: Domain100, XYZ_0: Domain100) -> Range100:
149 """
150 Compute the *whiteness* index :math:`WI` of the specified sample *CIE XYZ*
151 tristimulus values using the *Taube (1960)* method.
153 Parameters
154 ----------
155 XYZ
156 *CIE XYZ* tristimulus values of the sample.
157 XYZ_0
158 *CIE XYZ* tristimulus values of the reference white.
160 Returns
161 -------
162 :class:`numpy.ndarray`
163 *Whiteness* index :math:`WI`.
165 Notes
166 -----
167 +------------+-----------------------+---------------+
168 | **Domain** | **Scale - Reference** | **Scale - 1** |
169 +============+=======================+===============+
170 | ``XYZ`` | 100 | 1 |
171 +------------+-----------------------+---------------+
172 | ``XYZ_0`` | 100 | 1 |
173 +------------+-----------------------+---------------+
175 +------------+-----------------------+---------------+
176 | **Range** | **Scale - Reference** | **Scale - 1** |
177 +============+=======================+===============+
178 | ``WI`` | 100 | 1 |
179 +------------+-----------------------+---------------+
181 - *Whiteness* :math:`WI` values larger than 100 indicate a bluish
182 white and values smaller than 100 indicate a yellowish white.
184 References
185 ----------
186 :cite:`X-Rite2012a`
188 Examples
189 --------
190 >>> import numpy as np
191 >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000])
192 >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595])
193 >>> whiteness_Taube1960(XYZ, XYZ_0) # doctest: +ELLIPSIS
194 91.4071738...
195 """
197 _X, Y, Z = tsplit(to_domain_100(XYZ))
198 _X_0, _Y_0, Z_0 = tsplit(to_domain_100(XYZ_0))
200 WI = 400 * (Z / Z_0) - 3 * Y
202 return as_float(from_range_100(WI))
205def whiteness_Stensby1968(
206 Lab: Domain100,
207) -> Range100:
208 """
209 Compute the *whiteness* index :math:`WI` of the specified sample
210 *CIE L\\*a\\*b\\** colourspace array using the *Stensby (1968)* method.
212 Parameters
213 ----------
214 Lab
215 *CIE L\\*a\\*b\\** colourspace array of the sample.
217 Returns
218 -------
219 :class:`numpy.ndarray`
220 *Whiteness* index :math:`WI`.
222 Notes
223 -----
224 +------------+-----------------------+-----------------+
225 | **Domain** | **Scale - Reference** | **Scale - 1** |
226 +============+=======================+=================+
227 | ``Lab`` | 100 | 1 |
228 +------------+-----------------------+-----------------+
230 +------------+-----------------------+-----------------+
231 | **Range** | **Scale - Reference** | **Scale - 1** |
232 +============+=======================+=================+
233 | ``WI`` | 100 | 1 |
234 +------------+-----------------------+-----------------+
236 - *Whiteness* :math:`WI` values larger than 100 indicate a bluish
237 white and values smaller than 100 indicate a yellowish white.
239 References
240 ----------
241 :cite:`X-Rite2012a`
243 Examples
244 --------
245 >>> import numpy as np
246 >>> Lab = np.array([100.00000000, -2.46875131, -16.72486654])
247 >>> whiteness_Stensby1968(Lab) # doctest: +ELLIPSIS
248 142.7683456...
249 """
251 L, a, b = tsplit(to_domain_100(Lab))
253 WI = L - 3 * b + 3 * a
255 return as_float(from_range_100(WI))
258def whiteness_ASTME313(XYZ: Domain100) -> Range100:
259 """
260 Compute the *whiteness* index :math:`WI` of the specified sample *CIE XYZ*
261 tristimulus values using the *ASTM E313* method.
263 Parameters
264 ----------
265 XYZ
266 *CIE XYZ* tristimulus values of the sample.
268 Returns
269 -------
270 :class:`numpy.ndarray`
271 *Whiteness* index :math:`WI`.
273 Notes
274 -----
275 +------------+-----------------------+---------------+
276 | **Domain** | **Scale - Reference** | **Scale - 1** |
277 +============+=======================+===============+
278 | ``XYZ`` | 100 | 1 |
279 +------------+-----------------------+---------------+
281 +------------+-----------------------+---------------+
282 | **Range** | **Scale - Reference** | **Scale - 1** |
283 +============+=======================+===============+
284 | ``WI`` | 100 | 1 |
285 +------------+-----------------------+---------------+
287 References
288 ----------
289 :cite:`X-Rite2012a`
291 Examples
292 --------
293 >>> import numpy as np
294 >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000])
295 >>> whiteness_ASTME313(XYZ) # doctest: +ELLIPSIS
296 55.7400000...
297 """
299 _X, Y, Z = tsplit(to_domain_100(XYZ))
301 WI = 3.388 * Z - 3 * Y
303 return as_float(from_range_100(WI))
306def whiteness_Ganz1979(xy: ArrayLike, Y: Domain100) -> Range100:
307 """
308 Compute the *whiteness* index :math:`W` and *tint* :math:`T` of the
309 specified sample *CIE xy* chromaticity coordinates using the
310 *Ganz and Griesser (1979)* method.
312 Parameters
313 ----------
314 xy
315 Chromaticity coordinates *CIE xy* of the sample.
316 Y
317 Tristimulus :math:`Y` value of the sample.
319 Returns
320 -------
321 :class:`numpy.ndarray`
322 *Whiteness* :math:`W` and *tint* :math:`T`.
324 Notes
325 -----
326 +------------+-----------------------+---------------+
327 | **Domain** | **Scale - Reference** | **Scale - 1** |
328 +============+=======================+===============+
329 | ``Y`` | 100 | 1 |
330 +------------+-----------------------+---------------+
332 +------------+-----------------------+---------------+
333 | **Range** | **Scale - Reference** | **Scale - 1** |
334 +============+=======================+===============+
335 | ``WT`` | 100 | 1 |
336 +------------+-----------------------+---------------+
338 - The formula coefficients are valid for
339 *CIE Standard Illuminant D Series* *D65* and
340 *CIE 1964 10 Degree Standard Observer*.
341 - Positive output *tint* :math:`T` values indicate a greener tint
342 while negative values indicate a redder tint.
343 - Whiteness differences of less than 5 Ganz units appear to be
344 indistinguishable to the human eye.
345 - Tint differences of less than 0.5 Ganz units appear to be
346 indistinguishable to the human eye.
348 References
349 ----------
350 :cite:`X-Rite2012a`
352 Examples
353 --------
354 >>> import numpy as np
355 >>> xy = np.array([0.3167, 0.3334])
356 >>> whiteness_Ganz1979(xy, 100) # doctest: +ELLIPSIS
357 array([ 85.6003766..., 0.6789003...])
358 """
360 x, y = tsplit(xy)
361 Y = to_domain_100(Y)
363 W = Y - 1868.322 * x - 3695.690 * y + 1809.441
364 T = -1001.223 * x + 748.366 * y + 68.261
366 WT = tstack([W, T])
368 return from_range_100(WT)
371def whiteness_CIE2004(
372 xy: ArrayLike,
373 Y: Domain100,
374 xy_n: ArrayLike,
375 observer: Literal[
376 "CIE 1931 2 Degree Standard Observer",
377 "CIE 1964 10 Degree Standard Observer",
378 ] = ("CIE 1931 2 Degree Standard Observer"),
379) -> Range100:
380 """
381 Compute the *whiteness* :math:`W` or :math:`W_{10}` and *tint*
382 :math:`T` or :math:`T_{10}` of the specified sample *CIE xy* chromaticity
383 coordinates using the *CIE 2004* method.
385 Parameters
386 ----------
387 xy
388 Chromaticity coordinates *CIE xy* of the sample.
389 Y
390 Tristimulus :math:`Y` value of the sample.
391 xy_n
392 Chromaticity coordinates *xy_n* of a perfect diffuser.
393 observer
394 *CIE Standard Observer* used for computations, *tint* :math:`T` or
395 :math:`T_{10}` value is dependent on viewing field angular subtense.
397 Returns
398 -------
399 :class:`numpy.ndarray`
400 *Whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or
401 :math:`T_{10}` of the specified sample.
403 Notes
404 -----
405 +------------+-----------------------+---------------+
406 | **Domain** | **Scale - Reference** | **Scale - 1** |
407 +============+=======================+===============+
408 | ``Y`` | 100 | 1 |
409 +------------+-----------------------+---------------+
411 +------------+-----------------------+---------------+
412 | **Range** | **Scale - Reference** | **Scale - 1** |
413 +============+=======================+===============+
414 | ``WT`` | 100 | 1 |
415 +------------+-----------------------+---------------+
417 - This method may be used only for samples whose values of :math:`W`
418 or :math:`W_{10}` lie within the following limits: greater than 40
419 and less than 5Y - 280, or 5Y10 - 280.
420 - This method may be used only for samples whose values of :math:`T`
421 or :math:`T_{10}` lie within the following limits: greater than -4
422 and less than +2.
423 - Output *whiteness* :math:`W` or :math:`W_{10}` values larger than
424 100 indicate a bluish white while values smaller than 100 indicate
425 a yellowish white.
426 - Positive output *tint* :math:`T` or :math:`T_{10}` values indicate
427 a greener tint while negative values indicate a redder tint.
429 References
430 ----------
431 :cite:`CIETC1-482004k`
433 Examples
434 --------
435 >>> import numpy as np
436 >>> xy = np.array([0.3167, 0.3334])
437 >>> xy_n = np.array([0.3139, 0.3311])
438 >>> whiteness_CIE2004(xy, 100, xy_n) # doctest: +ELLIPSIS
439 array([ 93.85..., -1.305...])
440 """
442 x, y = tsplit(xy)
443 Y = to_domain_100(Y)
444 x_n, y_n = tsplit(xy_n)
446 W = Y + 800 * (x_n - x) + 1700 * (y_n - y)
447 T = (1000 if "1931" in observer else 900) * (x_n - x) - 650 * (y_n - y)
449 WT = tstack([W, T])
451 return from_range_100(WT)
454WHITENESS_METHODS: CanonicalMapping = CanonicalMapping(
455 {
456 "Berger 1959": whiteness_Berger1959,
457 "Taube 1960": whiteness_Taube1960,
458 "Stensby 1968": whiteness_Stensby1968,
459 "ASTM E313": whiteness_ASTME313,
460 "Ganz 1979": whiteness_Ganz1979,
461 "CIE 2004": whiteness_CIE2004,
462 }
463)
464WHITENESS_METHODS.__doc__ = """
465Supported *whiteness* computation methods.
467References
468----------
469:cite:`CIETC1-482004k`, :cite:`X-Rite2012a`
471Aliases:
473- 'cie2004': 'CIE 2004'
474"""
475WHITENESS_METHODS["cie2004"] = WHITENESS_METHODS["CIE 2004"]
478def whiteness(
479 XYZ: Domain100,
480 XYZ_0: ArrayLike,
481 method: (
482 Literal[
483 "ASTM E313",
484 "CIE 2004",
485 "Berger 1959",
486 "Ganz 1979",
487 "Stensby 1968",
488 "Taube 1960",
489 ]
490 | str
491 ) = "CIE 2004",
492 **kwargs: Any,
493) -> Range100:
494 """
495 Compute the *whiteness* index :math:`W` of the specified sample *CIE XYZ*
496 tristimulus values using the specified method.
498 Parameters
499 ----------
500 XYZ
501 *CIE XYZ* tristimulus values of the sample.
502 XYZ_0
503 *CIE XYZ* tristimulus values of the reference white.
504 method
505 Computation method.
507 Other Parameters
508 ----------------
509 observer
510 {:func:`colour.colorimetry.whiteness_CIE2004`},
511 *CIE Standard Observer* used for computations, *tint* :math:`T` or
512 :math:`T_{10}` value is dependent on viewing field angular subtense.
514 Returns
515 -------
516 :class:`numpy.ndarray`
517 *Whiteness* index :math:`W`.
519 Notes
520 -----
521 +------------+-----------------------+---------------+
522 | **Domain** | **Scale - Reference** | **Scale - 1** |
523 +============+=======================+===============+
524 | ``XYZ`` | 100 | 1 |
525 +------------+-----------------------+---------------+
526 | ``XYZ_0`` | 100 | 1 |
527 +------------+-----------------------+---------------+
529 +------------+-----------------------+---------------+
530 | **Range** | **Scale - Reference** | **Scale - 1** |
531 +============+=======================+===============+
532 | ``W`` | 100 | 1 |
533 +------------+-----------------------+---------------+
535 References
536 ----------
537 :cite:`CIETC1-482004k`, :cite:`Wyszecki2000ba`, :cite:`X-Rite2012a`,
538 :cite:`Wikipedia2004b`
540 Examples
541 --------
542 >>> import numpy as np
543 >>> from colour.models import xyY_to_XYZ
544 >>> XYZ = xyY_to_XYZ(np.array([0.3167, 0.3334, 100]))
545 >>> XYZ_0 = xyY_to_XYZ(np.array([0.3139, 0.3311, 100]))
546 >>> whiteness(XYZ, XYZ_0) # doctest: +ELLIPSIS
547 array([ 93.85..., -1.305...])
548 >>> XYZ = np.array([95.00000000, 100.00000000, 105.00000000])
549 >>> XYZ_0 = np.array([94.80966767, 100.00000000, 107.30513595])
550 >>> whiteness(XYZ, XYZ_0, method="Taube 1960") # doctest: +ELLIPSIS
551 91.4071738...
552 """
554 XYZ = as_float_array(XYZ)
555 XYZ_0 = as_float_array(XYZ_0)
557 method = validate_method(method, tuple(WHITENESS_METHODS))
559 kwargs.update({"XYZ": XYZ, "XYZ_0": XYZ_0})
561 function = WHITENESS_METHODS[method]
563 if function is whiteness_Stensby1968:
564 from colour.models import XYZ_to_Lab, XYZ_to_xy # noqa: PLC0415
566 # XYZ_to_Lab always expects XYZ in [0, 1], convert from reference [0, 100].
567 kwargs.update(
568 {
569 "Lab": XYZ_to_Lab(
570 XYZ / 100 if get_domain_range_scale() == "reference" else XYZ,
571 XYZ_to_xy(
572 XYZ_0 / 100
573 if get_domain_range_scale() == "reference"
574 else XYZ_0
575 ),
576 )
577 }
578 )
579 elif function in (whiteness_Ganz1979, whiteness_CIE2004):
580 from colour.models import XYZ_to_xy # noqa: PLC0415
582 _X_0, Y_0, _Z_0 = tsplit(XYZ_0)
583 kwargs.update({"xy": XYZ_to_xy(XYZ), "Y": Y_0, "xy_n": XYZ_to_xy(XYZ_0)})
585 return function(**filter_kwargs(function, **kwargs))