Coverage for appearance/rlab.py: 32%

1"""

2 RLAB Colour Appearance Model

3============================

4

5 Define the *RLAB* colour appearance model for predicting perceptual colour

6 attributes under varying viewing conditions.

7

8- :attr:`colour.VIEWING_CONDITIONS_RLAB`

9- :attr:`colour.D_FACTOR_RLAB`

10- :class:`colour.CAM_Specification_RLAB`

11- :func:`colour.XYZ_to_RLAB`

12

13 References

14----------

15- :cite:`Fairchild1996a` : Fairchild, M. D. (1996). Refinement of the RLAB

16 color space. Color Research & Application, 21(5), 338-346.

17 doi:10.1002/(SICI)1520-6378(199610)21:5<338::AID-COL3>3.0.CO;2-Z

18- :cite:`Fairchild2013w` : Fairchild, M. D. (2013). The RLAB Model. In Color

19 Appearance Models (3rd ed., pp. 5563-5824). Wiley. ISBN:B00DAYO8E2

20"""

21

22 from __future__ import annotations

23

24 from dataclasses import dataclass, field

25

26 import numpy as np

27

28 from colour.algebra import sdiv, sdiv_mode, spow, vecmul

29 from colour.appearance.hunt import MATRIX_XYZ_TO_HPE, XYZ_to_rgb

30 from colour.hints import Annotated, ArrayLike, Domain100, NDArrayFloat # noqa: TC001

31 from colour.utilities import (

32 CanonicalMapping,

33 MixinDataclassArray,

34 as_float,

35 as_float_array,

36 from_range_degrees,

37 row_as_diagonal,

38 to_domain_100,

39 tsplit,

40)

41

42 __author__ = "Colour Developers"

43 __copyright__ = "Copyright 2013 Colour Developers"

44 __license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"

45 __maintainer__ = "Colour Developers"

46 __email__ = "colour-developers@colour-science.org"

47 __status__ = "Production"

48

49 __all__ = [

50 "MATRIX_R",

51 "VIEWING_CONDITIONS_RLAB",

52 "D_FACTOR_RLAB",

53 "CAM_ReferenceSpecification_RLAB",

54 "CAM_Specification_RLAB",

55 "XYZ_to_RLAB",

56]

57

58 MATRIX_R: NDArrayFloat = np.array(

59 [

60 [1.9569, -1.1882, 0.2313],

61 [0.3612, 0.6388, 0.0000],

62 [0.0000, 0.0000, 1.0000],

63 ]

64)

65"""*RLAB* colour appearance model precomputed helper matrix."""

66

67 VIEWING_CONDITIONS_RLAB: CanonicalMapping = CanonicalMapping(

68 {"Average": 1 / 2.3, "Dim": 1 / 2.9, "Dark": 1 / 3.5}

69)

70 VIEWING_CONDITIONS_RLAB.__doc__ = """

71 Define the reference *RLAB* colour appearance model viewing conditions.

72

73 References

74----------

75:cite:`Fairchild1996a`, :cite:`Fairchild2013w`

76"""

77

78 D_FACTOR_RLAB: CanonicalMapping = CanonicalMapping(

79 {

80 "Hard Copy Images": 1,

81 "Soft Copy Images": 0,

82 "Projected Transparencies, Dark Room": 0.5,

83 }

84)

85 D_FACTOR_RLAB.__doc__ = """

86 Define the *RLAB* colour appearance model *Discounting-the-Illuminant*

87 factor values for the specified media types.

88

89 References

90----------

91:cite:`Fairchild1996a`, :cite:`Fairchild2013w`

92

93 Aliases:

94

95- 'hard_cp_img': 'Hard Copy Images'

96- 'soft_cp_img': 'Soft Copy Images'

97- 'projected_dark': 'Projected Transparencies, Dark Room'

98"""

99 D_FACTOR_RLAB["hard_cp_img"] = D_FACTOR_RLAB["Hard Copy Images"]

100 D_FACTOR_RLAB["soft_cp_img"] = D_FACTOR_RLAB["Soft Copy Images"]

101 D_FACTOR_RLAB["projected_dark"] = D_FACTOR_RLAB["Projected Transparencies, Dark Room"]

102

103

104@dataclass

105 class CAM_ReferenceSpecification_RLAB(MixinDataclassArray):

106 """

107 Define the *RLAB* colour appearance model reference specification.

108

109 This specification contains field names consistent with the *Fairchild

110 (2013)* reference.

111

112 Parameters

113 ----------

114 LR

115 Correlate of *lightness* :math:`L^R`.

116 CR

117 Correlate of *achromatic chroma* :math:`C^R`.

118 hR

119 *Hue* angle :math:`h^R` in degrees.

120 sR

121 Correlate of *saturation* :math:`s^R`.

122 HR

123 *Hue* :math:`h` composition :math:`H^R`.

124 aR

125 Red-green chromatic response :math:`a^R`.

126 bR

127 Yellow-blue chromatic response :math:`b^R`.

128

129 References

130 ----------

131 :cite:`Fairchild1996a`, :cite:`Fairchild2013w`

132 """

133

134 LR: float | NDArrayFloat | None = field(default_factory=lambda: None)

135 CR: float | NDArrayFloat | None = field(default_factory=lambda: None)

136 hR: float | NDArrayFloat | None = field(default_factory=lambda: None)

137 sR: float | NDArrayFloat | None = field(default_factory=lambda: None)

138 HR: float | NDArrayFloat | None = field(default_factory=lambda: None)

139 aR: float | NDArrayFloat | None = field(default_factory=lambda: None)

140 bR: float | NDArrayFloat | None = field(default_factory=lambda: None)

141

142

143@dataclass

144 class CAM_Specification_RLAB(MixinDataclassArray):

145 """

146 Define the *RLAB* colour appearance model specification.

147

148 This specification provides a standardized interface for the *RLAB* model

149 with field names consistent across all colour appearance models in

150 :mod:`colour.appearance`. While the field names differ from the original

151 *Fairchild (2013)* reference notation, they map directly to the model's

152 perceptual correlates.

153

154 Parameters

155 ----------

156 J

157 Correlate of *lightness* :math:`L^R`.

158 C

159 Correlate of *achromatic chroma* :math:`C^R`.

160 h

161 *Hue* angle :math:`h^R` in degrees.

162 s

163 Correlate of *saturation* :math:`s^R`.

164 HC

165 *Hue* :math:`h` composition :math:`H^C`.

166 a

167 Red-green chromatic response :math:`a^R`.

168 b

169 Yellow-blue chromatic response :math:`b^R`.

170

171 Notes

172 -----

173 - This specification is the one used in the current model

174 implementation.

175

176 References

177 ----------

178 :cite:`Fairchild1996a`, :cite:`Fairchild2013w`

179 """

180

181 J: NDArrayFloat | None = field(default_factory=lambda: None)

182 C: NDArrayFloat | None = field(default_factory=lambda: None)

183 h: NDArrayFloat | None = field(default_factory=lambda: None)

184 s: NDArrayFloat | None = field(default_factory=lambda: None)

185 HC: NDArrayFloat | None = field(default_factory=lambda: None)

186 a: NDArrayFloat | None = field(default_factory=lambda: None)

187 b: NDArrayFloat | None = field(default_factory=lambda: None)

188

189

190 def XYZ_to_RLAB(

191 XYZ: Domain100,

192 XYZ_n: Domain100,

193 Y_n: ArrayLike,

194 sigma: ArrayLike = VIEWING_CONDITIONS_RLAB["Average"],

195 D: ArrayLike = D_FACTOR_RLAB["Hard Copy Images"],

196) -> Annotated[CAM_Specification_RLAB, 360]:

197 """

198 Compute the *RLAB* colour appearance model correlates from the specified

199 *CIE XYZ* tristimulus values.

200

201 Parameters

202 ----------

203 XYZ

204 *CIE XYZ* tristimulus values of test sample / stimulus.

205 XYZ_n

206 *CIE XYZ* tristimulus values of reference white.

207 Y_n

208 Absolute adapting luminance in :math:`cd/m^2`.

209 sigma

210 Relative luminance of the surround, see

211 :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference.

212 D

213 *Discounting-the-Illuminant* factor normalised to domain [0, 1].

214

215 Returns

216 -------

217 CAM_Specification_RLAB

218 *RLAB* colour appearance model specification.

219

220 Notes

221 -----

222 +---------------------+-----------------------+---------------+

223 | **Domain** | **Scale - Reference** | **Scale - 1** |

224 +=====================+=======================+===============+

225 | ``XYZ`` | 100 | 1 |

226 +---------------------+-----------------------+---------------+

227 | ``XYZ_n`` | 100 | 1 |

228 +---------------------+-----------------------+---------------+

229

230 +---------------------+-----------------------+---------------+

231 | **Range** | **Scale - Reference** | **Scale - 1** |

232 +=====================+=======================+===============+

233 | ``specification.h`` | 360 | 1 |

234 +---------------------+-----------------------+---------------+

235

236 References

237 ----------

238 :cite:`Fairchild1996a`, :cite:`Fairchild2013w`

239

240 Examples

241 --------

242 >>> XYZ = np.array([19.01, 20.00, 21.78])

243 >>> XYZ_n = np.array([109.85, 100, 35.58])

244 >>> Y_n = 31.83

245 >>> sigma = VIEWING_CONDITIONS_RLAB["Average"]

246 >>> D = D_FACTOR_RLAB["Hard Copy Images"]

247 >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS

248 CAM_Specification_RLAB(J=49.8347069..., C=54.8700585..., \

249 h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., \

250 b=-52.6142956...)

251 """

252

253 XYZ = to_domain_100(XYZ)

254 XYZ_n = to_domain_100(XYZ_n)

255 Y_n = as_float_array(Y_n)

256 D = as_float_array(D)

257 sigma = as_float_array(sigma)

258

259 # Converting to cone responses.

260 LMS_n = XYZ_to_rgb(XYZ_n)

261

262 # Computing the :math:`A` matrix.

263 LMS_l_E = 3 * LMS_n / np.sum(LMS_n, axis=-1)[..., None]

264 LMS_p_L = (1 + spow(Y_n[..., None], 1 / 3) + LMS_l_E) / (

265 1 + spow(Y_n[..., None], 1 / 3) + 1 / LMS_l_E

266 )

267

268 LMS_a_L = (LMS_p_L + D[..., None] * (1 - LMS_p_L)) / LMS_n

269

270 M = np.matmul(np.matmul(MATRIX_R, row_as_diagonal(LMS_a_L)), MATRIX_XYZ_TO_HPE)

271 XYZ_ref = vecmul(M, XYZ)

272

273 Y_ref: NDArrayFloat

274 X_ref, Y_ref, Z_ref = tsplit(XYZ_ref)

275

276 # Computing the correlate of *Lightness* :math:`L^R`.

277 LR = 100 * spow(Y_ref, sigma)

278

279 # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.

280 aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma))

281 bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma))

282

283 # Computing the *hue* angle :math:`h^R`.

284 hR = np.degrees(np.arctan2(bR, aR)) % 360

285 # TODO: Implement hue composition computation.

286

287 # Computing the correlate of *chroma* :math:`C^R`.

288 CR = np.hypot(aR, bR)

289

290 # Computing the correlate of *saturation* :math:`s^R`.

291 with sdiv_mode():

292 sR = sdiv(CR, LR)

293

294 return CAM_Specification_RLAB(

295 J=LR,

296 C=CR,

297 h=as_float(from_range_degrees(hR)),

298 s=sR,

299 HC=None,

300 a=as_float(aR),

301 b=as_float(bR),

302 )