Last active
September 8, 2021 17:44
-
-
Save facelessuser/4f2eab96411bbb7cadfd57cfea233ac7 to your computer and use it in GitHub Desktop.
Din99o Lab and Lch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Din99o class. | |
https://de.wikipedia.org/wiki/DIN99-Farbraum | |
""" | |
from coloraide.spaces import RE_DEFAULT_MATCH | |
from coloraide.spaces.xyz import XYZ | |
from coloraide.spaces.lch.base import LchBase | |
from coloraide.spaces.lab.base import LabBase, lab_to_xyz, xyz_to_lab | |
from coloraide import util | |
import re | |
import math | |
ACHROMATIC_THRESHOLD = 0.000000000002 | |
KE = 1 | |
KCH = 1 | |
# --- Din99o --- | |
RADS = math.radians(26) | |
FACTOR = 0.83 | |
C1 = 303.67 | |
C2 = 0.0039 | |
C3 = 0.075 | |
C4 = 0.0435 | |
# --- Din99 --- | |
# RADS = math.radians(16) | |
# FACTOR = 0.7 | |
# C1 = 105.51 | |
# C2 = 0.0158 | |
# C3 = 0.045 | |
# C4 = 0.045 | |
def lab_to_din99o(lab): | |
"""XYZ to Din99o.""" | |
l, a, b = lab | |
l99o = C1 * math.log(1 + C2 * l) / KE | |
if a == 0 and b == 0: | |
a99o = b99o = 0 | |
else: | |
eo = a * math.cos(RADS) + b * math.sin(RADS) | |
fo = FACTOR * (b * math.cos(RADS) - a * math.sin(RADS)) | |
go = math.sqrt(eo ** 2 + fo ** 2) | |
c99o = math.log(1 + C3 * go) / (C4 * KE * KCH) | |
h99o = math.atan2(fo, eo) + RADS | |
a99o = c99o * math.cos(h99o) | |
b99o = c99o * math.sin(h99o) | |
return [l99o, a99o, b99o] | |
def din99o_lab_to_lch(lab): | |
""" | |
Convert Din99o Lab to Lch. | |
Hue is in radians. | |
""" | |
l99o, a99o, b99o = lab | |
h99o = math.atan2(b99o, a99o) | |
c99o = math.sqrt(a99o ** 2 + b99o ** 2) | |
return [l99o, c99o, h99o] | |
def din99o_to_lab(din99o): | |
"""Din99o to XYZ.""" | |
l99o, c99o, h99o = din99o_lab_to_lch(din99o) | |
g = (math.exp(C4 * c99o * KCH * KE) - 1) / C3 | |
e = g * math.cos(h99o - RADS) | |
f = g * math.sin(h99o - RADS) | |
return [ | |
(math.exp((l99o * KE) / C1) - 1) / C2, | |
e * math.cos(RADS) - (f / FACTOR) * math.sin(RADS), | |
e * math.sin(RADS) + (f / FACTOR) * math.cos(RADS) | |
] | |
class Din99o(LabBase): | |
"""Din99o class.""" | |
SPACE = "din99o-lab" | |
SERIALIZE = ("--din99o-lab",) | |
CHANNEL_NAMES = ("lightness", "a", "b", "alpha") | |
DEFAULT_MATCH = re.compile(RE_DEFAULT_MATCH.format(color_space='|'.join(SERIALIZE), channels=3)) | |
WHITE = "D65" | |
@classmethod | |
def _to_xyz(cls, parent, lab): | |
"""To XYZ.""" | |
return parent.chromatic_adaptation(cls.WHITE, XYZ.WHITE, lab_to_xyz(din99o_to_lab(lab), cls.white())) | |
@classmethod | |
def _from_xyz(cls, parent, xyz): | |
"""From XYZ.""" | |
return lab_to_din99o(xyz_to_lab(parent.chromatic_adaptation(XYZ.WHITE, cls.WHITE, xyz), cls.white())) | |
def lch_to_lab(lch): | |
"""Din99o Lch to lab.""" | |
l, c, h = lch | |
h = util.no_nan(h) | |
# If, for whatever reason (mainly direct user input), | |
# if chroma is less than zero, clamp to zero. | |
if c < 0.0: | |
c = 0.0 | |
return [ | |
l, | |
c * math.cos(math.radians(h)), | |
c * math.sin(math.radians(h)) | |
] | |
def lab_to_lch(lab): | |
"""Din99o Lab to Lch.""" | |
l, a, b = lab | |
h = math.degrees(math.atan2(b, a)) | |
c = math.sqrt(a ** 2 + b ** 2) | |
# Achromatic colors will often get extremely close, but not quite hit zero. | |
# Essentially, we want to discard noise through rounding and such. | |
if c <= ACHROMATIC_THRESHOLD: | |
h = util.NaN | |
return [l, c, util.constrain_hue(h)] | |
class Din99oLch(LchBase): | |
"""Lch D65 class.""" | |
SPACE = "din99o-lch" | |
SERIALIZE = ("--din99o-lch",) | |
DEFAULT_MATCH = re.compile(RE_DEFAULT_MATCH.format(color_space='|'.join(SERIALIZE), channels=3)) | |
WHITE = "D65" | |
@classmethod | |
def _to_din99o_lab(cls, parent, lch): | |
"""To Lab.""" | |
return lch_to_lab(lch) | |
@classmethod | |
def _from_din99o_lab(cls, parent, lab): | |
"""To Lab.""" | |
return lab_to_lch(lab) | |
@classmethod | |
def _to_xyz(cls, parent, lch): | |
"""To XYZ.""" | |
return Din99o._to_xyz(parent, cls._to_din99o_lab(parent, lch)) | |
@classmethod | |
def _from_xyz(cls, parent, xyz): | |
"""From XYZ.""" | |
return cls._from_din99o_lab(parent, Din99o._from_xyz(parent, xyz)) | |
Color("blue").interpolate( | |
"white", | |
space='lab' | |
) | |
Color("blue").interpolate( | |
"white", | |
space='din99o-lab' | |
) | |
Color("blue").interpolate( | |
"white", | |
space='jzazbz' | |
) | |
Color("blue").interpolate( | |
"white", | |
space='oklab' | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment