1515- https://github.com/Grkmyldz148/helmlab
1616"""
1717from __future__ import annotations
18- from functools import lru_cache
1918from .lab import Lab
2019from ..cat import WHITES
2120from ..channels import Channel , FLG_MIRROR_PERCENT
22- from .. import util
2321from .. import algebra as alg
24- from ..types import Vector , Matrix
22+ from ..types import Vector
23+
2524
2625M1 = [
27- [ 0.4407412072890238 , 0.40911369156796634 , 0.18687249931895067 ],
28- [ 0.12308224353121994 , 0.557136239636739 , 0.19274910862205916 ],
29- [- 0.23021079382916068 , 0.9278243045135821 , 0.4854100909928004 ]
26+ [0.758376129483666 , 0.38380162590825073 , - 0.09608055040602373 ],
27+ [0.1267139363153284 , 0.8421628149123207 , 0.03434823621506485 ],
28+ [0.07639223722200054 , 0.25894352627545103 , 0.6139139663787314 ]
3029]
30+
3131M1_INV = [
32- [2.126067208590642 , - 0.584957446988563 , - 0.5862125072812663 ],
33- [- 2.416561715802903 , 5.96398983868483 , - 1.4378868725604865 ],
34- [5.627382627163576 , - 11.677133103760319 , 4.530507259029062 ]
32+ [1.4133073795748359 , - 0.7245661027731641 , 0.2617287231983285 ],
33+ [- 0.20907372745004316 , 1.3153903462455017 , - 0.10631661879545858 ],
34+ [- 0.08767910052303855 , - 0.46465890124976855 , 1.641168001772807 ]
3535]
36+
3637M2 = [
37- [ 0.2778609560084774 , 0.21180362605092856 , 0.6372017137356791 ],
38- [ 1.7548720474157444 , - 0.9793270531556616 , - 0.7760752041286899 ],
39- [- 2.418690735750103 , 3.982044105359993 , - 1.2833774660668076 ]
38+ [0.10058070589596234 , 1.0155897099394149 , - 0.1161704158353769 ],
39+ [2.361576469961645 , - 2.4409973750629357 , 0.07942090510129071 ],
40+ [0.04565327074453785 , 0.818754884454245 , - 0.8644081551987829 ]
4041]
42+
4143M2_INV = [
42- [0.8649568923272442 , 0.5589393137919957 , 0.09145639155676465 ],
43- [0.8215892255459026 , 0.2356964021282657 , 0.2653934155119385 ],
44- [0.9190914921797732 , - 0.3220781744225231 , - 0.1280967178320807 ]
44+ [0.9999999999999997 , 0.3827736318539182 , - 0.09922417671418936 ],
45+ [0.9999999999999996 , - 0.039921540824987126 , - 0.13806096115936264 ],
46+ [0.9999999999999997 , - 0.017597113360184317 , - 1.292870720601441 ]
4547]
4648
4749
48- def xyz_d65_to_helmlab (xyz : Vector ) -> Vector :
49- """Convert XYZ to Helmlab ."""
50+ def xyz_d65_to_helmgen (xyz : Vector ) -> Vector :
51+ """Convert XYZ to Helmgen ."""
5052
5153 lms = alg .matmul (M1 , xyz , dims = alg .D2_D1 )
5254 c = [alg .nth_root (v , 3 ) for v in lms ]
5355 return alg .matmul (M2 , c , dims = alg .D2_D1 )
5456
5557
56- def helmlab_to_xyz (lab : Vector ) -> Vector :
57- """Convert Helmlab to XYZ."""
58+ def helmgen_to_xyz (lab : Vector ) -> Vector :
59+ """Convert Helmgen to XYZ."""
5860
5961 c = alg .matmul (M2_INV , lab , dims = alg .D2_D1 )
6062 lms = [alg .spow (v , 3 ) for v in c ]
6163 return alg .matmul (M1_INV , lms , dims = alg .D2_D1 )
6264
6365
64- @lru_cache (maxsize = 1 )
65- def get_nc_lut () -> Matrix :
66- """
67- Run pipeline without NC on D65 neutrals to measure achromatic error.
68-
69- ```
70- nc = [[ncl, nca, ncb], ...]
71- ```
72- """
73-
74- n = 256
75- nc = alg .zeros ((n , 3 ))
76- x , y , z = util .xy_to_xyz (WHITES ['2deg' ]['ASTM-E308-D65' ])
77- for i in range (n ):
78- y = i / (n - 1 )
79- xyz = [y * x , y , y * z ]
80-
81- # Pipeline without NC
82- nc [i ][:] = xyz_d65_to_helmlab (xyz )
83- return nc
84-
85-
86- def neutral_error (l : float , nc : Matrix | None = None ) -> Vector :
87- """Neutral correction error."""
88-
89- if nc is None :
90- nc = get_nc_lut ()
91-
92- n = len (nc )
93-
94- if l <= 0 :
95- return [0.0 , 0.0 ]
96-
97- if l < nc [0 ][0 ]:
98- t = l / nc [0 ][0 ]
99- return [nc [0 ][1 ] * t , nc [0 ][2 ] * t ]
100-
101- if l >= nc [n - 1 ][0 ]:
102- return nc [n - 1 ][1 :]
103-
104- lo , hi = 0 , n - 1
105- while (hi - lo ) > 1 :
106- mid = (lo + hi ) >> 1
107- if nc [mid ][0 ] <= l :
108- lo = mid
109- else :
110- hi = mid
111-
112- t = (l - nc [lo ][0 ]) / (nc [lo + 1 ][0 ] - nc [lo ][0 ])
113- return [
114- nc [lo ][1 ] + t * (nc [lo + 1 ][1 ] - nc [lo ][1 ]),
115- nc [lo ][2 ] + t * (nc [lo + 1 ][2 ] - nc [lo ][2 ])
116- ]
117-
118-
119- def xyz_d65_to_helmlab_corrected (xyz : Vector ) -> Vector :
120- """Convert XYZ to Helmlab."""
121-
122- lab = xyz_d65_to_helmlab (xyz )
123-
124- # Stage 10: Neutral correction (LUT)
125- a_err , b_err = neutral_error (lab [0 ])
126- lab [1 ] -= a_err
127- lab [2 ] -= b_err
128- return lab
129-
130-
131- def helmlab_corrected_to_xyz (lab : Vector ) -> Vector :
132- """Convert XYZ to Helmlab."""
133-
134- # Stage 10: Neutral correction (LUT)
135- a_err , b_err = neutral_error (lab [0 ])
136- lab [1 ] += a_err
137- lab [2 ] += b_err
138-
139- return helmlab_to_xyz (lab )
140-
141-
14266class Helmgen (Lab ):
14367 """Helmgen class."""
14468
14569 BASE = "xyz-d65"
14670 NAME = "helmgen"
14771 SERIALIZE = ("--helmgen" ,)
14872 CHANNELS = (
149- Channel ("l" , 0.0 , 1.168140042703694 ),
73+ Channel ("l" , 0.0 , 1.0 ),
15074 Channel ("a" , - 0.4 , 0.4 , flags = FLG_MIRROR_PERCENT ),
15175 Channel ("b" , - 0.4 , 0.4 , flags = FLG_MIRROR_PERCENT )
15276 )
@@ -155,9 +79,9 @@ class Helmgen(Lab):
15579 def to_base (self , coords : Vector ) -> Vector :
15680 """To XYZ."""
15781
158- return helmlab_corrected_to_xyz (coords )
82+ return helmgen_to_xyz (coords )
15983
16084 def from_base (self , coords : Vector ) -> Vector :
16185 """From XYZ."""
16286
163- return xyz_d65_to_helmlab_corrected (coords )
87+ return xyz_d65_to_helmgen (coords )
0 commit comments