/
oklab.py
170 lines (138 loc) · 4.98 KB
/
oklab.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"""
Oklab Colourspace
=================
Defines the *Oklab* colourspace transformations:
- :func:`colour.XYZ_to_Oklab`
- :func:`colour.Oklab_to_XYZ`
References
----------
- :cite:`Ottosson2020` : Ottosson, B. (2020). A perceptual color space for
image processing. Retrieved December 24, 2020, from
https://bottosson.github.io/posts/oklab/
"""
from __future__ import annotations
from functools import partial
import numpy as np
from colour.algebra import spow
from colour.hints import ArrayLike, NDArrayFloat
from colour.models import Iab_to_XYZ, XYZ_to_Iab
__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"
__all__ = [
"MATRIX_1_XYZ_TO_LMS",
"MATRIX_1_LMS_TO_XYZ",
"MATRIX_2_LMS_TO_LAB",
"MATRIX_2_LAB_TO_LMS",
"XYZ_to_Oklab",
"Oklab_to_XYZ",
]
MATRIX_1_XYZ_TO_LMS: NDArrayFloat = np.array(
[
[0.8189330101, 0.3618667424, -0.1288597137],
[0.0329845436, 0.9293118715, 0.0361456387],
[0.0482003018, 0.2643662691, 0.6338517070],
]
)
"""*CIE XYZ* tristimulus values to normalised cone responses matrix."""
MATRIX_1_LMS_TO_XYZ: NDArrayFloat = np.linalg.inv(MATRIX_1_XYZ_TO_LMS)
"""Normalised cone responses to *CIE XYZ* tristimulus values matrix."""
MATRIX_2_LMS_TO_LAB: NDArrayFloat = np.array(
[
[0.2104542553, 0.7936177850, -0.0040720468],
[1.9779984951, -2.4285922050, 0.4505937099],
[0.0259040371, 0.7827717662, -0.8086757660],
]
)
"""Normalised cone responses to *Oklab* colourspace matrix."""
MATRIX_2_LAB_TO_LMS: NDArrayFloat = np.linalg.inv(MATRIX_2_LMS_TO_LAB)
"""*Oklab* colourspace to normalised cone responses matrix."""
def XYZ_to_Oklab(XYZ: ArrayLike) -> NDArrayFloat:
"""
Convert from *CIE XYZ* tristimulus values to *Oklab* colourspace.
Parameters
----------
XYZ
*CIE XYZ* tristimulus values.
Returns
-------
:class:`numpy.ndarray`
*Oklab* colourspace array.
Notes
-----
+------------+-----------------------+-----------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``XYZ`` | [0, 1] | [0, 1] |
+------------+-----------------------+-----------------+
+------------+-----------------------+-----------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``Lab`` | ``L`` : [0, 1] | ``L`` : [0, 1] |
| | | |
| | ``a`` : [-1, 1] | ``a`` : [-1, 1] |
| | | |
| | ``b`` : [-1, 1] | ``b`` : [-1, 1] |
+------------+-----------------------+-----------------+
- Input *CIE XYZ* tristimulus values must be adapted to
*CIE Standard Illuminant D Series* *D65*.
References
----------
:cite:`Ottosson2020`
Examples
--------
>>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
>>> XYZ_to_Oklab(XYZ) # doctest: +ELLIPSIS
array([ 0.5163401..., 0.154695 ..., 0.0628957...])
"""
return XYZ_to_Iab(
XYZ,
partial(spow, p=1 / 3),
MATRIX_1_XYZ_TO_LMS,
MATRIX_2_LMS_TO_LAB,
)
def Oklab_to_XYZ(Lab: ArrayLike) -> NDArrayFloat:
"""
Convert from *Oklab* colourspace to *CIE XYZ* tristimulus values.
Parameters
----------
Lab
*Oklab* colourspace array.
Returns
-------
:class:`numpy.ndarray`
*CIE XYZ* tristimulus values.
Notes
-----
+------------+-----------------------+-----------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``Lab`` | ``L`` : [0, 1] | ``L`` : [0, 1] |
| | | |
| | ``a`` : [-1, 1] | ``a`` : [-1, 1] |
| | | |
| | ``b`` : [-1, 1] | ``b`` : [-1, 1] |
+------------+-----------------------+-----------------+
+------------+-----------------------+-----------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+=================+
| ``XYZ`` | [0, 1] | [0, 1] |
+------------+-----------------------+-----------------+
References
----------
:cite:`Ottosson2020`
Examples
--------
>>> Lab = np.array([0.51634019, 0.15469500, 0.06289579])
>>> Oklab_to_XYZ(Lab) # doctest: +ELLIPSIS
array([ 0.2065400..., 0.1219722..., 0.0513695...])
"""
return Iab_to_XYZ(
Lab,
partial(spow, p=3),
MATRIX_2_LAB_TO_LMS,
MATRIX_1_LMS_TO_XYZ,
)