Coverage for colour/recovery/tests/test_meng2015.py: 100%
36 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"""Define the unit tests for the :mod:`colour.recovery.meng2015` module."""
3from __future__ import annotations
5import numpy as np
6import pytest
8from colour.colorimetry import (
9 MSDS_CMFS,
10 SDS_ILLUMINANTS,
11 SpectralShape,
12 reshape_msds,
13 reshape_sd,
14 sd_to_XYZ_integration,
15)
16from colour.constants import TOLERANCE_ABSOLUTE_TESTS
17from colour.recovery import XYZ_to_sd_Meng2015
18from colour.utilities import domain_range_scale, is_scipy_installed
20__author__ = "Colour Developers"
21__copyright__ = "Copyright 2013 Colour Developers"
22__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
23__maintainer__ = "Colour Developers"
24__email__ = "colour-developers@colour-science.org"
25__status__ = "Production"
27__all__ = [
28 "TestXYZ_to_sd_Meng2015",
29]
32class TestXYZ_to_sd_Meng2015:
33 """
34 Define :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition unit
35 tests methods.
36 """
38 def setup_method(self) -> None:
39 """Initialise the common tests attributes."""
41 self._cmfs = reshape_msds(
42 MSDS_CMFS["CIE 1931 2 Degree Standard Observer"],
43 SpectralShape(360, 780, 10),
44 )
45 self._sd_D65 = reshape_sd(SDS_ILLUMINANTS["D65"], self._cmfs.shape)
46 self._sd_E = reshape_sd(SDS_ILLUMINANTS["E"], self._cmfs.shape)
48 def test_XYZ_to_sd_Meng2015(self) -> None:
49 """Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition."""
51 if not is_scipy_installed(): # pragma: no cover
52 return
54 XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
55 np.testing.assert_allclose(
56 sd_to_XYZ_integration(
57 XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_D65),
58 self._cmfs,
59 self._sd_D65,
60 )
61 / 100,
62 XYZ,
63 atol=TOLERANCE_ABSOLUTE_TESTS,
64 )
66 np.testing.assert_allclose(
67 sd_to_XYZ_integration(
68 XYZ_to_sd_Meng2015(XYZ, self._cmfs, self._sd_E),
69 self._cmfs,
70 self._sd_E,
71 )
72 / 100,
73 XYZ,
74 atol=TOLERANCE_ABSOLUTE_TESTS,
75 )
77 np.testing.assert_allclose(
78 sd_to_XYZ_integration(
79 XYZ_to_sd_Meng2015(
80 XYZ,
81 self._cmfs,
82 self._sd_D65,
83 optimisation_kwargs={
84 "options": {
85 "ftol": 1e-10,
86 }
87 },
88 ),
89 self._cmfs,
90 self._sd_D65,
91 )
92 / 100,
93 XYZ,
94 atol=TOLERANCE_ABSOLUTE_TESTS,
95 )
97 shape = SpectralShape(400, 700, 5)
98 cmfs = reshape_msds(self._cmfs, shape)
99 np.testing.assert_allclose(
100 sd_to_XYZ_integration(
101 XYZ_to_sd_Meng2015(XYZ, cmfs, self._sd_D65), cmfs, self._sd_D65
102 )
103 / 100,
104 XYZ,
105 atol=TOLERANCE_ABSOLUTE_TESTS,
106 )
108 def test_raise_exception_XYZ_to_sd_Meng2015(self) -> None:
109 """
110 Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015`
111 definition raised exception.
112 """
114 if not is_scipy_installed(): # pragma: no cover
115 return
117 pytest.raises(
118 RuntimeError,
119 XYZ_to_sd_Meng2015,
120 np.array([0.0, 0.0, 1.0]),
121 optimisation_kwargs={
122 "options": {"maxiter": 10},
123 },
124 )
126 def test_domain_range_scale_XYZ_to_sd_Meng2015(self) -> None:
127 """
128 Test :func:`colour.recovery.meng2015.XYZ_to_sd_Meng2015` definition
129 domain and range scale support.
130 """
132 if not is_scipy_installed(): # pragma: no cover
133 return
135 XYZ_i = np.array([0.20654008, 0.12197225, 0.05136952])
136 XYZ_o = sd_to_XYZ_integration(
137 XYZ_to_sd_Meng2015(XYZ_i, self._cmfs, self._sd_D65),
138 self._cmfs,
139 self._sd_D65,
140 )
142 d_r = (("reference", 1, 1), ("1", 1, 0.01), ("100", 100, 1))
143 for scale, factor_a, factor_b in d_r:
144 with domain_range_scale(scale):
145 np.testing.assert_allclose(
146 sd_to_XYZ_integration(
147 XYZ_to_sd_Meng2015(XYZ_i * factor_a, self._cmfs, self._sd_D65),
148 self._cmfs,
149 self._sd_D65,
150 ),
151 XYZ_o * factor_b,
152 atol=TOLERANCE_ABSOLUTE_TESTS,
153 )