@texel/color 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,509 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- """
4
- Copyright (c) 2021 Björn Ottosson
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy of
7
- this software and associated documentation files (the "Software"), to deal in
8
- the Software without restriction, including without limitation the rights to
9
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
- of the Software, and to permit persons to whom the Software is furnished to do
11
- so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
23
-
24
- **Overview**
25
-
26
- This script is pulled from Coloraide, and modified slightly by @mattdesl for JS output.
27
- https://github.com/facelessuser/coloraide
28
-
29
- The gamut approximation code was originally developed by Björn Ottosson.
30
-
31
- Original file is located at
32
- https://colab.research.google.com/drive/1JdXHhEyjjEE--19ZPH1bZV_LiGQBndzs
33
-
34
- This notebook was used to compute coefficients for the compute_max_saturation function in this blog post:
35
- http://bottosson.github.io/posts/gamutclipping/
36
-
37
- The code is available for reference, since it could be useful to derive coefficients for other color spaces. It was
38
- written quickly to derive the values and both structure and documentation is poor.
39
- """
40
-
41
- # Commented out `IPython` magic to ensure Python compatibility.
42
- import numpy as np
43
- import scipy.optimize
44
- import matplotlib.pyplot as plt
45
- import sys
46
- import os
47
- import json
48
- from coloraide import algebra as alg
49
- sys.path.insert(0, os.getcwd())
50
-
51
- # Use higher precision Oklab conversion matrix along with LMS matrix with our exact white point
52
- from tools.calc_oklab_matrices import xyzt_white_d65, xyzt_white_d50, xyzt_get_matrix, SRGBL_TO_LMS, LMS_TO_SRGBL, LMS3_TO_OKLAB, OKLAB_TO_LMS3, LMS_TO_XYZD50, XYZD50_TO_LMS # noqa: E402
53
-
54
- PRINT_DIAGS = False
55
-
56
- # Recalculated for consistent reference white
57
- # see https://github.com/w3c/csswg-drafts/issues/6642#issuecomment-943521484
58
- XYZ_TO_LMS = [
59
- [ 0.8190224379967030, 0.3619062600528904, -0.1288737815209879 ],
60
- [ 0.0329836539323885, 0.9292868615863434, 0.0361446663506424 ],
61
- [ 0.0481771893596242, 0.2642395317527308, 0.6335478284694309 ],
62
- ]
63
- # inverse of XYZtoLMS_M
64
- LMS_TO_XYZ = [
65
- [ 1.2268798758459243, -0.5578149944602171, 0.2813910456659647 ],
66
- [ -0.0405757452148008, 1.1122868032803170, -0.0717110580655164 ],
67
- [ -0.0763729366746601, -0.4214933324022432, 1.5869240198367816 ],
68
- ]
69
- LMS3_TO_OKLAB = [
70
- [ 0.2104542683093140, 0.7936177747023054, -0.0040720430116193 ],
71
- [ 1.9779985324311684, -2.4285922420485799, 0.4505937096174110 ],
72
- [ 0.0259040424655478, 0.7827717124575296, -0.8086757549230774 ],
73
- ]
74
- # LMStoIab_M inverted
75
- OKLAB_TO_LMS3 = [
76
- [ 1.0000000000000000, 0.3963377773761749, 0.2158037573099136 ],
77
- [ 1.0000000000000000, -0.1055613458156586, -0.0638541728258133 ],
78
- [ 1.0000000000000000, -0.0894841775298119, -1.2914855480194092 ],
79
- ]
80
-
81
-
82
- def print_matrix (a, b, arr):
83
- data = json.dumps(arr.tolist(), indent=2, separators=(',', ': '))
84
- suffix = '_M'
85
- print(f'export const {a}_to_{b}{suffix} = {data};\n')
86
-
87
- def print_rational (a, b, rstr):
88
- suffix = '_M'
89
- print(f'export const {a}_to_{b}{suffix} = {eval(rstr)};\n')
90
-
91
-
92
- def print_json (label, data):
93
- str = json.dumps(data, indent=2, separators=(',', ': '))
94
- print(f'export const {label} = {str};\n')
95
-
96
- def do_calc(GAMUT = 'srgb'):
97
- global SRGBL_TO_LMS, LMS_TO_SRGBL, LMS3_TO_OKLAB, OKLAB_TO_LMS3, XYZD50_TO_LMS, LMS_TO_XYZD50, XYZD50_TO_LMS
98
- np.set_printoptions(precision=8)
99
-
100
- var_name = 'linear_sRGB'
101
- if GAMUT == 'display-p3':
102
- var_name = 'linear_DisplayP3'
103
- elif GAMUT == 'rec2020':
104
- var_name = 'linear_Rec2020'
105
- elif GAMUT == 'a98-rgb':
106
- var_name = 'linear_A98RGB'
107
- elif GAMUT == 'prophoto-rgb':
108
- var_name = 'linear_ProPhotoRGB'
109
-
110
- white = xyzt_white_d50 if GAMUT == 'prophoto-rgb' else xyzt_white_d65
111
- whitepoint = 'D50' if GAMUT == 'prophoto-rgb' else 'D65'
112
- RGBL_TO_XYZ, XYZ_TO_RGBL = xyzt_get_matrix(white, GAMUT)
113
-
114
- """
115
- Hard coding the matrices using rational numbers to match CSS working draft spec.
116
- https://github.com/w3c/csswg-drafts/pull/7320
117
- https://drafts.csswg.org/css-color-4/#color-conversion-code
118
- """
119
- RGBL_TO_XYZ_RATIONAL = ""
120
- XYZ_TO_RGBL_RATIONAL = ""
121
-
122
- if GAMUT == 'srgb':
123
- RGBL_TO_XYZ_RATIONAL = """[
124
- [ 506752 / 1228815, 87881 / 245763, 12673 / 70218 ],
125
- [ 87098 / 409605, 175762 / 245763, 12673 / 175545 ],
126
- [ 7918 / 409605, 87881 / 737289, 1001167 / 1053270 ],
127
- ]"""
128
- XYZ_TO_RGBL_RATIONAL = """[
129
- [ 12831 / 3959, -329 / 214, -1974 / 3959 ],
130
- [ -851781 / 878810, 1648619 / 878810, 36519 / 878810 ],
131
- [ 705 / 12673, -2585 / 12673, 705 / 667 ],
132
- ]"""
133
- elif GAMUT == 'rec2020':
134
- RGBL_TO_XYZ_RATIONAL = """[
135
- [ 63426534 / 99577255, 20160776 / 139408157, 47086771 / 278816314 ],
136
- [ 26158966 / 99577255, 472592308 / 697040785, 8267143 / 139408157 ],
137
- [ 0 / 1, 19567812 / 697040785, 295819943 / 278816314 ],
138
- ]"""
139
- XYZ_TO_RGBL_RATIONAL = """[
140
- [ 30757411 / 17917100, -6372589 / 17917100, -4539589 / 17917100 ],
141
- [ -19765991 / 29648200, 47925759 / 29648200, 467509 / 29648200 ],
142
- [ 792561 / 44930125, -1921689 / 44930125, 42328811 / 44930125 ],
143
- ]"""
144
- elif GAMUT == 'a98-rgb':
145
- # convert an array of linear-light a98-rgb values to CIE XYZ
146
- # http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
147
- # has greater numerical precision than section 4.3.5.3 of
148
- # https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
149
- # but the values below were calculated from first principles
150
- # from the chromaticity coordinates of R G B W
151
- RGBL_TO_XYZ_RATIONAL = """[
152
- [ 573536 / 994567, 263643 / 1420810, 187206 / 994567 ],
153
- [ 591459 / 1989134, 6239551 / 9945670, 374412 / 4972835 ],
154
- [ 53769 / 1989134, 351524 / 4972835, 4929758 / 4972835 ],
155
- ]"""
156
- XYZ_TO_RGBL_RATIONAL = """[
157
- [ 1829569 / 896150, -506331 / 896150, -308931 / 896150 ],
158
- [ -851781 / 878810, 1648619 / 878810, 36519 / 878810 ],
159
- [ 16779 / 1248040, -147721 / 1248040, 1266979 / 1248040 ],
160
- ]"""
161
- elif GAMUT == 'display-p3':
162
- # TODO: Evaluate whether this is really superior to what coloraide suggests
163
- # Right now it is needed to ensure accuracy with Colorjs.io
164
- # However, it is not clear how they have computed the results
165
- RGBL_TO_XYZ_RATIONAL = """[
166
- [ 608311 / 1250200, 189793 / 714400, 198249 / 1000160 ],
167
- [ 35783 / 156275, 247089 / 357200, 198249 / 2500400 ],
168
- [ 0 / 1, 32229 / 714400, 5220557 / 5000800 ],
169
- ]"""
170
- XYZ_TO_RGBL_RATIONAL = """[
171
- [ 446124 / 178915, -333277 / 357830, -72051 / 178915 ],
172
- [ -14852 / 17905, 63121 / 35810, 423 / 17905 ],
173
- [ 11844 / 330415, -50337 / 660830, 316169 / 330415 ],
174
- ]"""
175
- elif GAMUT == 'prophoto-rgb':
176
- # override from https://github.com/w3c/csswg-drafts/issues/7675
177
- # rational form exceeds JavaScript precision:
178
- # https://github.com/w3c/csswg-drafts/pull/7320
179
- RGBL_TO_XYZ = [
180
- [ 0.79776664490064230, 0.13518129740053308, 0.03134773412839220 ],
181
- [ 0.28807482881940130, 0.71183523424187300, 0.00008993693872564 ],
182
- [ 0.00000000000000000, 0.00000000000000000, 0.82510460251046020 ]
183
- ]
184
- XYZ_TO_RGBL = [
185
- [ 1.34578688164715830, -0.25557208737979464, -0.05110186497554526 ],
186
- [ -0.54463070512490190, 1.50824774284514680, 0.02052744743642139 ],
187
- [ 0.00000000000000000, 0.00000000000000000, 1.21196754563894520 ]
188
- ]
189
-
190
- if len(XYZ_TO_RGBL_RATIONAL) > 0:
191
- XYZ_TO_RGBL = eval(XYZ_TO_RGBL_RATIONAL)
192
- if len(RGBL_TO_XYZ_RATIONAL) > 0:
193
- RGBL_TO_XYZ = eval(RGBL_TO_XYZ_RATIONAL)
194
-
195
- # Calculate the gamut <-> LMS matrices to adjust the working gamut
196
- if GAMUT == 'srgb':
197
- RGBL_TO_LMS = SRGBL_TO_LMS
198
- LMS_TO_RGBL = LMS_TO_SRGBL
199
- elif GAMUT == 'prophoto-rgb':
200
- # Note: this is not currently used in the final results as ProPhoto gamut is not yet supported
201
- RGBL_TO_LMS = alg.matmul(XYZD50_TO_LMS, RGBL_TO_XYZ)
202
- LMS_TO_RGBL = alg.inv(RGBL_TO_LMS)
203
- else:
204
- RGBL_TO_LMS = alg.matmul(XYZ_TO_LMS, RGBL_TO_XYZ)
205
- LMS_TO_RGBL = alg.inv(RGBL_TO_LMS)
206
-
207
- def printarray (label, arr):
208
- print(label, '[ ' + ', '.join([str(n) for n in arr]) + ' ]')
209
-
210
- # print('RGBL_TO_LMS',RGBL_TO_LMS)
211
- # print('LMS_TO_RGBL', LMS_TO_RGBL)
212
-
213
- RGBL_TO_LMS = np.asfarray(RGBL_TO_LMS)
214
- LMS_TO_RGBL = np.asfarray(LMS_TO_RGBL)
215
- LMS3_TO_OKLAB = np.asfarray(LMS3_TO_OKLAB)
216
- OKLAB_TO_LMS3 = np.asfarray(OKLAB_TO_LMS3)
217
-
218
- def linear_srgb_to_oklab(c):
219
- l = RGBL_TO_LMS[0][0] * c[0, ...] + RGBL_TO_LMS[0][1] * c[1, ...] + RGBL_TO_LMS[0][2] * c[2, ...]
220
- m = RGBL_TO_LMS[1][0] * c[0, ...] + RGBL_TO_LMS[1][1] * c[1, ...] + RGBL_TO_LMS[1][2] * c[2, ...]
221
- s = RGBL_TO_LMS[2][0] * c[0, ...] + RGBL_TO_LMS[2][1] * c[1, ...] + RGBL_TO_LMS[2][2] * c[2, ...]
222
-
223
- l_ = np.cbrt(l)
224
- m_ = np.cbrt(m)
225
- s_ = np.cbrt(s)
226
-
227
- return np.array([
228
- LMS3_TO_OKLAB[0][0] * l_ + LMS3_TO_OKLAB[0][1] * m_ + LMS3_TO_OKLAB[0][2] * s_,
229
- LMS3_TO_OKLAB[1][0] * l_ + LMS3_TO_OKLAB[1][1] * m_ + LMS3_TO_OKLAB[1][2] * s_,
230
- LMS3_TO_OKLAB[2][0] * l_ + LMS3_TO_OKLAB[2][1] * m_ + LMS3_TO_OKLAB[2][2] * s_,
231
- ])
232
-
233
- # define functions for R, G and B as functions of S, h (with L = 1 and S = C/L)
234
-
235
- def to_lms(S, h):
236
- a = S * np.cos(h)
237
- b = S * np.sin(h)
238
-
239
- l_ = OKLAB_TO_LMS3[0][0] + OKLAB_TO_LMS3[0][1] * a + OKLAB_TO_LMS3[0][2] * b
240
- m_ = OKLAB_TO_LMS3[1][0] + OKLAB_TO_LMS3[1][1] * a + OKLAB_TO_LMS3[1][2] * b
241
- s_ = OKLAB_TO_LMS3[2][0] + OKLAB_TO_LMS3[2][1] * a + OKLAB_TO_LMS3[2][2] * b
242
-
243
- l = l_ * l_ * l_
244
- m = m_ * m_ * m_
245
- s = s_ * s_ * s_
246
-
247
- return (l, m, s)
248
-
249
- def to_lms_dS(S, h):
250
- a = S * np.cos(h)
251
- b = S * np.sin(h)
252
-
253
- l_ = OKLAB_TO_LMS3[0][0] + OKLAB_TO_LMS3[0][1] * a + OKLAB_TO_LMS3[0][2] * b
254
- m_ = OKLAB_TO_LMS3[1][0] + OKLAB_TO_LMS3[1][1] * a + OKLAB_TO_LMS3[1][2] * b
255
- s_ = OKLAB_TO_LMS3[2][0] + OKLAB_TO_LMS3[2][1] * a + OKLAB_TO_LMS3[2][2] * b
256
-
257
- l = (LMS3_TO_OKLAB[0][1] * np.cos(h) + LMS3_TO_OKLAB[0][1] * np.sin(h)) * 3 * l_* l_
258
- m = (LMS3_TO_OKLAB[1][1] * np.cos(h) + LMS3_TO_OKLAB[1][1] * np.sin(h)) * 3 * m_* m_
259
- s = (LMS3_TO_OKLAB[2][1] * np.cos(h) + LMS3_TO_OKLAB[2][1] * np.sin(h)) * 3 * s_* s_
260
-
261
- return (l, m, s)
262
-
263
- def to_lms_dS2(S, h):
264
- a = S * np.cos(h)
265
- b = S * np.sin(h)
266
-
267
- l_ = OKLAB_TO_LMS3[0][0] + OKLAB_TO_LMS3[0][1] * a + OKLAB_TO_LMS3[0][2] * b
268
- m_ = OKLAB_TO_LMS3[1][0] + OKLAB_TO_LMS3[1][1] * a + OKLAB_TO_LMS3[1][2] * b
269
- s_ = OKLAB_TO_LMS3[2][0] + OKLAB_TO_LMS3[2][1] * a + OKLAB_TO_LMS3[2][2] * b
270
-
271
- l = (LMS3_TO_OKLAB[0][1] * np.cos(h) + LMS3_TO_OKLAB[0][2] * np.sin(h)) ** 2 * 6 * l_
272
- m = (LMS3_TO_OKLAB[1][1] * np.cos(h) + LMS3_TO_OKLAB[0][2] * np.sin(h)) ** 2 * 6 * m_
273
- s = (LMS3_TO_OKLAB[2][1] * np.cos(h) + LMS3_TO_OKLAB[0][2] * np.sin(h)) ** 2 * 6 * s_
274
-
275
- return (l, m, s)
276
-
277
-
278
- def to_R(S, h):
279
- (l, m, s) = to_lms(S, h)
280
- return LMS_TO_RGBL[0][0] * l + LMS_TO_RGBL[0][1] * m + LMS_TO_RGBL[0][2] * s
281
-
282
- def to_R_dS(S, h):
283
- (l, m, s) = to_lms_dS(S, h)
284
- return LMS_TO_RGBL[0][0] * l + LMS_TO_RGBL[0][1] * m + LMS_TO_RGBL[0][2] * s
285
-
286
- def to_R_dS2(S, h):
287
- (l, m, s) = to_lms_dS2(S, h)
288
- return LMS_TO_RGBL[0][0] * l + LMS_TO_RGBL[0][1] * m + LMS_TO_RGBL[0][2] * s
289
-
290
- def to_G(S, h):
291
- (l, m, s) = to_lms(S, h)
292
- return LMS_TO_RGBL[1][0] * l + LMS_TO_RGBL[1][1] * m + LMS_TO_RGBL[1][2] * s
293
-
294
- def to_G_dS(S, h):
295
- (l, m, s) = to_lms_dS(S, h)
296
- return LMS_TO_RGBL[1][0] * l + LMS_TO_RGBL[1][1] * m + LMS_TO_RGBL[1][2] * s
297
-
298
- def to_G_dS2(S, h):
299
- (l, m, s) = to_lms_dS2(S, h)
300
- return LMS_TO_RGBL[1][0] * l + LMS_TO_RGBL[1][1] * m + LMS_TO_RGBL[1][2] * s
301
-
302
- def to_B(S, h):
303
- (l, m, s) = to_lms(S, h)
304
- return LMS_TO_RGBL[2][0] * l + LMS_TO_RGBL[2][1] * m + LMS_TO_RGBL[2][2] * s
305
-
306
- def to_B_dS(S, h):
307
- (l, m, s) = to_lms_dS(S, h)
308
- return LMS_TO_RGBL[2][0] * l + LMS_TO_RGBL[2][1] * m + LMS_TO_RGBL[2][2] * s
309
-
310
- def to_B_dS2(S, h):
311
- (l, m, s) = to_lms_dS2(S, h)
312
- return LMS_TO_RGBL[2][0] * l + LMS_TO_RGBL[2][1] * m + LMS_TO_RGBL[2][2] * s
313
-
314
- if GAMUT != 'prophoto-rgb':
315
- hs, Ss = np.meshgrid(np.linspace(-np.pi, np.pi, 720), np.linspace(0, 1, 200))
316
-
317
- Rs = to_R(Ss, hs)
318
- Gs = to_G(Ss, hs)
319
- Bs = to_B(Ss, hs)
320
-
321
- gamut = np.minimum(Rs, np.minimum(Gs, Bs))
322
-
323
- r_lab = linear_srgb_to_oklab(np.array([1, 0, 0]))
324
- g_lab = linear_srgb_to_oklab(np.array([0, 1, 0]))
325
- b_lab = linear_srgb_to_oklab(np.array([0, 0, 1]))
326
-
327
- r_h = np.arctan2(r_lab[2], r_lab[1])
328
- g_h = np.arctan2(g_lab[2], g_lab[1])
329
- b_h = np.arctan2(b_lab[2], b_lab[1])
330
-
331
- r_dir = 0.5 * np.array([np.cos(b_h) + np.cos(g_h), np.sin(b_h) + np.sin(g_h)])
332
- g_dir = 0.5 * np.array([np.cos(b_h) + np.cos(r_h), np.sin(b_h) + np.sin(r_h)])
333
- b_dir = 0.5 * np.array([np.cos(r_h) + np.cos(g_h), np.sin(r_h) + np.sin(g_h)])
334
-
335
- r_dir /= r_dir[0]** 2 + r_dir[1]** 2
336
- g_dir /= g_dir[0]** 2 + g_dir[1]** 2
337
- b_dir /= b_dir[0]** 2 + b_dir[1]** 2
338
-
339
- # These are coefficients to quickly test which component goes below zero first.
340
- # Used like this in compute_max_saturation:
341
- # if (-1.88170328f * a - 0.80936493f * b > 1) // Red component goes below zero first
342
-
343
- r_hs, r_Ss = np.meshgrid(np.linspace(g_h, 2 * np.pi + b_h, 200), np.linspace(0, 1, 200))
344
-
345
- r_Rs = to_R(r_Ss, r_hs)
346
-
347
- g_hs, g_Ss = np.meshgrid(np.linspace(b_h, r_h, 200), np.linspace(0, 1, 200))
348
-
349
- g_Gs = to_G(g_Ss, g_hs)
350
-
351
- b_hs, b_Ss = np.meshgrid(np.linspace(r_h, g_h, 200), np.linspace(0, 1, 200))
352
-
353
- b_Bs = to_B(b_Ss, b_hs)
354
-
355
- # These are numerical fits to the edge of the chroma
356
- # The resulting coefficient, x_R, x_G and x_B are used in compute_max_saturation, as values for k0
357
- its = 1
358
-
359
- resolution = 100000
360
-
361
- h = np.linspace(g_h, 2 * np.pi + b_h, resolution)
362
- a = np.cos(h)
363
- b = np.sin(h)
364
-
365
- def e_R(x):
366
- S = x[0] + x[1] * a + x[2] * b + x[3] * a ** 2 + x[4] * a * b
367
- S = np.maximum(0, S)
368
-
369
- # optimize for solution that is easiest to solve with one step Haley's method
370
- f = to_R(S, h)
371
- f1 = to_R_dS(S, h)
372
- f2 = to_R_dS2(S, h)
373
- S_1 = S - f * f1 / (f1 ** 2 - f * f2 / 2)
374
-
375
- f_ = to_R(S_1, h)
376
- return np.average(f_ ** 10) # + f_[0] ** 2 + f_[-1] ** 2
377
-
378
- x_R = scipy.optimize.minimize(e_R, np.array([1.19086277, 1.76576728, 0.59662641, 0.75515197, 0.56771245])).x
379
-
380
- # printarray('R COEFF', x_R)
381
-
382
- S_R = x_R[0] + x_R[1] * a + x_R[2] * b + x_R[3] * a ** 2 + x_R[4] * a * b
383
-
384
- S_R1 = S_R
385
- for i in range(0, its):
386
- f = to_R(S_R1, h)
387
- f1 = to_R_dS(S_R1, h)
388
- f2 = to_R_dS2(S_R1, h)
389
- S_R1 = S_R1 - f * f1 / (f1 ** 2 - f * f2 / (2))
390
-
391
- plt.plot(S_R1, 'r')
392
-
393
- #####
394
-
395
- h = np.linspace(b_h, r_h, resolution)
396
- a = np.cos(h)
397
- b = np.sin(h)
398
-
399
- def e_G(x):
400
- S = x[0] + x[1] * a + x[2] * b + x[3] * a ** 2 + x[4] * a * b
401
- S = np.maximum(0, S)
402
-
403
- # optimize for solution that is easiest to solve with one step Haley's method
404
- f = to_G(S, h)
405
- f1 = to_G_dS(S, h)
406
- f2 = to_G_dS2(S, h)
407
- S_1 = S - f * f1 / (f1 ** 2 - f * f2 / 2)
408
-
409
- f_ = to_G(S_1, h)
410
- return np.average(f_ ** 10) # + f_[0] ** 2 + f_[-1] ** 2
411
-
412
- x_G = scipy.optimize.minimize(e_G, np.array([0.73956515, -0.45954404, 0.08285427, 0.12541073, -0.14503204])).x
413
-
414
- # printarray('G COEFF', x_G)
415
-
416
- S_G = x_G[0] + x_G[1] * a + x_G[2] * b + x_G[3] * a ** 2 + x_G[4] * a * b
417
-
418
- S_G1 = S_G
419
- for i in range(0, its):
420
- f = to_G(S_G1, h)
421
- f1 = to_G_dS(S_G1, h)
422
- f2 = to_G_dS2(S_G1, h)
423
- S_G1 = S_G1 - f * f1 / (f1 ** 2 - f * f2 / (2))
424
-
425
-
426
- #####
427
-
428
- h = np.linspace(r_h, g_h, resolution)
429
- a = np.cos(h)
430
- b = np.sin(h)
431
-
432
- def e_B(x):
433
- S = x[0] + x[1] * a + x[2] * b + x[3] * a ** 2 + x[4] * a * b
434
- S = np.maximum(0, S)
435
-
436
- # optimize for solution that is easiest to solve with one step Haley's method
437
- f = to_B(S, h)
438
- f1 = to_B_dS(S, h)
439
- f2 = to_B_dS2(S, h)
440
- S_1 = S - f * f1 / (f1 ** 2 - f * f2 / 2)
441
-
442
- f_ = to_B(S_1, h)
443
- return np.average(f_ ** 10) # + f_[0] ** 2 + f_[-1] ** 2
444
-
445
- x_B = scipy.optimize.minimize(e_B, np.array([1.35733652, -0.00915799, -1.1513021, -0.50559606, 0.00692167])).x
446
-
447
- # printarray('B COEFF', x_B)
448
-
449
- S_B = x_B[0] + x_B[1] * a + x_B[2] * b + x_B[3] * a ** 2 + x_B[4] * a * b
450
-
451
- S_B1 = S_B
452
- for i in range(0, its):
453
- f = to_B(S_B1, h)
454
- f1 = to_B_dS(S_B1, h)
455
- f2 = to_B_dS2(S_B1, h)
456
- S_B1 = S_B1 - f * f1 / (f1 ** 2 - f * f2 / (2))
457
-
458
- print(f'// {var_name} space\n')
459
-
460
- print(f'// {var_name} to XYZ ({whitepoint}) matrices\n')
461
- if len(RGBL_TO_XYZ_RATIONAL) > 0:
462
- print_rational(var_name, 'XYZ', RGBL_TO_XYZ_RATIONAL)
463
- else:
464
- print_matrix(var_name, 'XYZ', np.asfarray(RGBL_TO_XYZ))
465
-
466
- if len(XYZ_TO_RGBL_RATIONAL) > 0:
467
- print_rational('XYZ', var_name, XYZ_TO_RGBL_RATIONAL)
468
- else:
469
- print_matrix('XYZ', var_name, np.asfarray(XYZ_TO_RGBL))
470
-
471
- print(f'// {var_name} to LMS matrices\n')
472
- print_matrix(var_name, 'LMS', RGBL_TO_LMS)
473
- print_matrix('LMS', var_name, LMS_TO_RGBL)
474
-
475
- if GAMUT != 'prophoto-rgb':
476
- coeff = [
477
- [
478
- r_dir.tolist(),
479
- x_R.tolist(),
480
- ],
481
- [
482
- g_dir.tolist(),
483
- x_G.tolist()
484
- ],
485
- [
486
- b_dir.tolist(),
487
- x_B.tolist()
488
- ]
489
- ]
490
- print(f'// {var_name} coefficients for OKLab gamut approximation\n')
491
- print_json(f'OKLab_to_{var_name}_coefficients', coeff)
492
- else:
493
- print(f'// {var_name} does not yet support OKLab gamut approximation\n')
494
-
495
- # print things...
496
-
497
- print(f'/** This file is auto-generated by tools/print_matrices.py */\n')
498
- print(f'// OKLab to LMS matrices\n')
499
- print_matrix('OKLab', 'LMS', np.asfarray(OKLAB_TO_LMS3))
500
- print_matrix('LMS', 'OKLab', np.asfarray(LMS3_TO_OKLAB))
501
- print_matrix('XYZ', 'LMS', np.asfarray(XYZ_TO_LMS))
502
- print_matrix('LMS', 'XYZ', np.asfarray(LMS_TO_XYZ))
503
-
504
- # don't need these...
505
- # print_matrix('XYZD50', 'LMS', np.asfarray(XYZD50_TO_LMS))
506
- # print_matrix('LMS', 'XYZD50', np.asfarray(LMS_TO_XYZD50))
507
-
508
- for gamut in ['srgb', 'display-p3', 'rec2020', 'a98-rgb', 'prophoto-rgb']:
509
- d = do_calc(gamut)