Last active
November 20, 2022 18:44
-
-
Save lboulard/df1629f2e27034b123d67f5268aa1b3e to your computer and use it in GitHub Desktop.
RGB to xterm/oklch
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
ipython | |
matplotlib | |
black | |
isort |
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
# | |
# This file is autogenerated by pip-compile with python 3.10 | |
# To update, run: | |
# | |
# pip-compile --no-emit-trusted-host requirements-dev.in | |
# | |
asttokens==2.1.0 | |
# via stack-data | |
backcall==0.2.0 | |
# via ipython | |
black==22.10.0 | |
# via -r requirements-dev.in | |
click==8.1.3 | |
# via black | |
colorama==0.4.6 | |
# via | |
# click | |
# ipython | |
contourpy==1.0.6 | |
# via matplotlib | |
cycler==0.11.0 | |
# via matplotlib | |
decorator==5.1.1 | |
# via ipython | |
executing==1.2.0 | |
# via stack-data | |
fonttools==4.38.0 | |
# via matplotlib | |
ipython==8.6.0 | |
# via -r requirements-dev.in | |
isort==5.10.1 | |
# via -r requirements-dev.in | |
jedi==0.18.1 | |
# via ipython | |
kiwisolver==1.4.4 | |
# via matplotlib | |
matplotlib==3.6.2 | |
# via -r requirements-dev.in | |
matplotlib-inline==0.1.6 | |
# via ipython | |
mypy-extensions==0.4.3 | |
# via black | |
numpy==1.23.5 | |
# via | |
# contourpy | |
# matplotlib | |
packaging==21.3 | |
# via matplotlib | |
parso==0.8.3 | |
# via jedi | |
pathspec==0.10.2 | |
# via black | |
pickleshare==0.7.5 | |
# via ipython | |
pillow==9.3.0 | |
# via matplotlib | |
platformdirs==2.5.4 | |
# via black | |
prompt-toolkit==3.0.32 | |
# via ipython | |
pure-eval==0.2.2 | |
# via stack-data | |
pygments==2.13.0 | |
# via ipython | |
pyparsing==3.0.9 | |
# via | |
# matplotlib | |
# packaging | |
python-dateutil==2.8.2 | |
# via matplotlib | |
six==1.16.0 | |
# via python-dateutil | |
stack-data==0.6.1 | |
# via ipython | |
tomli==2.0.1 | |
# via black | |
traitlets==5.5.0 | |
# via | |
# ipython | |
# matplotlib-inline | |
wcwidth==0.2.5 | |
# via prompt-toolkit |
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
colour-science | |
numpy |
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
# | |
# This file is autogenerated by pip-compile with python 3.10 | |
# To update, run: | |
# | |
# pip-compile --no-emit-trusted-host requirements.in | |
# | |
colour-science==0.4.1 | |
# via -r requirements.in | |
imageio==2.22.4 | |
# via colour-science | |
numpy==1.23.5 | |
# via | |
# -r requirements.in | |
# colour-science | |
# imageio | |
# scipy | |
pillow==9.3.0 | |
# via imageio | |
scipy==1.9.3 | |
# via colour-science | |
typing-extensions==4.4.0 | |
# via colour-science |
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
from math import atan2, degrees, pow, sqrt | |
import colour | |
import numpy as np | |
def xtermLUT(readable_blue=True): | |
# 0 - 7 : primary | |
# 9 - 15 : primary (bright) | |
return np.uint8( | |
[ | |
[0, 0, 0], | |
[205, 0, 0], | |
[0, 205, 0], | |
[205, 205, 0], | |
[0, 0, 238], | |
[205, 0, 205], | |
[0, 205, 205], | |
[229, 229, 229], | |
] | |
+ [ | |
[127, 127, 127], | |
[255, 0, 0], | |
[0, 255, 0], | |
[255, 255, 0], | |
[92, 92, 255] if readable_blue else [0, 0, 255], | |
[255, 0, 255], | |
[0, 255, 255], | |
[255, 255, 255], | |
] | |
) | |
def vgaLUT(): | |
# 0 - 7 : primary | |
# 9 - 15 : primary (bright) | |
return np.uint8( | |
[ | |
[0, 0, 0], | |
[170, 0, 0], | |
[0, 170, 0], | |
[170, 85, 0], | |
[0, 0, 170], | |
[170, 0, 170], | |
[0, 170, 170], | |
[170, 170, 170], | |
] | |
+ [ | |
[85, 85, 85], | |
[255, 85, 85], | |
[85, 255, 85], | |
[255, 255, 85], | |
[85, 85, 255], | |
[255, 85, 255], | |
[85, 255, 255], | |
[255, 255, 255], | |
] | |
) | |
def xtermLUT(base=xtermLUT): | |
# 16 - 231 : 6x6x6 cube | |
# 232 - 255 : gray scale (23 colors) | |
lut = [] | |
m = [0, 0x5F, 0x87, 0xAF, 0xD7, 0xFF] | |
for r in range(0, 6): | |
r = m[r] | |
for g in range(0, 6): | |
g = m[g] | |
for b in range(0, 6): | |
lut.append([r, g, m[b]]) | |
assert len(lut) == 216 | |
# gray | |
for i in range(0, 24): | |
lut.append([8 + i * 10] * 3) | |
assert len(lut) == 240 | |
return np.concatenate((base(), np.uint8(lut)), axis=0) | |
def unhexrgb(s: str) -> list: | |
if s[0] == "#": | |
s = s[1:] | |
if len(s) == 3: | |
rgb = list(int(c, 16) for c in s) | |
elif len(s) == 6: | |
rgb = list(int(s[i : i + 2], 16) for i in range(0, 6, 2)) | |
else: | |
raise Exception(f"unknown color format {s}") | |
return np.uint8(rgb) | |
def hexrgb(rgb) -> str: | |
assert len(rgb) == 3 | |
assert all(0.0 <= x < 256.0 for x in rgb) | |
return f"#{int(rgb[0]):02X}{int(rgb[1]):02X}{int(rgb[2]):02X}" | |
def scale(rgb): | |
"""RGB 0 - 255 reduced to 0.0-1.0 scale.""" | |
return colour.io.convert_bit_depth(rgb, "float32") | |
def unscale(rgb): | |
"""RGB 0.0 - 1.0 reduced to 0-255 scale.""" | |
return colour.io.convert_bit_depth(rgb, "uint8") | |
def RGB_to_Oklab(rgb: list) -> list: | |
assert len(rgb) == 3 | |
assert all(0 <= x < 256 for x in rgb) | |
xyz = colour.sRGB_to_XYZ(scale(rgb)) | |
return colour.XYZ_to_Oklab(xyz) | |
def RGB_to_Oklch(rgb: list) -> list: | |
l, a, b = RGB_to_Oklab(rgb) | |
k1, k2, k3 = 0.206, 0.03, 1.206 / 1.03 | |
lr = (k3 * l - k1 + sqrt(pow(k3 * l - k1, 2) + 4 * k2 * k3 * l)) / 2 | |
c = sqrt(a * a + b * b) | |
h = degrees(atan2(b, a)) | |
if h < 0: | |
h += 360 | |
return [l, c, h] | |
def RGB_to_Lab(rgb: list) -> list: | |
assert len(rgb) == 3 | |
assert all(0 <= x < 256 for x in rgb) | |
return colour.XYZ_to_Lab(colour.sRGB_to_XYZ(scale(rgb))) | |
def Lab_to_RGB(rgb: list) -> list: | |
assert len(rgb) == 3 | |
return unscale(colour.XYZ_to_sRGB(colour.Lab_to_XYZ(rgb))) | |
XTERM_LAB = np.array([RGB_to_Lab(color) for color in xtermLUT()]) | |
def xterm_lut(rgb): | |
terms = XTERM_LAB[16:] | |
lab = RGB_to_Lab(rgb) | |
k, m = -1, 256 | |
for i, e in enumerate(colour.delta_E(lab, x) for x in terms): | |
if e < m: | |
k, m = i, e | |
return k + 16, xtermLUT()[k + 16] | |
def truecolor(rgb): | |
return f"\033[48;2;{rgb[0]};{rgb[1]};{rgb[2]}m" | |
def bg0(): | |
return f"\033[0m" | |
def display(colors): | |
terms = xtermLUT() | |
for name, color in colors.items(): | |
n, xterm_rgb = xterm_lut(color) | |
print(f"{name:<20s} {hexrgb(color)} {hexrgb(xterm_rgb)}/{n:3}", end=" ") | |
print( | |
f"{truecolor(color)} {bg0()} {truecolor(xterm_rgb)} {bg0()}", end=" | " | |
) | |
l, c, h = RGB_to_Oklch(color) | |
print(f"{l*100:5.1f}% {c:4.2f} {int(h):3}", end=" | ") | |
l, c, h = RGB_to_Oklch(xterm_rgb) | |
print(f"{l*100:5.1f}% {c:4.2f} {int(h):3}") | |
def main(): | |
print("----- yellow") | |
display( | |
{ | |
"central color": unhexrgb("#FF8700"), | |
"selected window bg": unhexrgb("#5F5FFF"), | |
"hidden window text": unhexrgb("#000087"), | |
"level 1 text": unhexrgb("#000087"), | |
"level 1 background": unhexrgb("#FFAF00"), | |
"level 2 text": unhexrgb("#000087"), | |
"level 2 background": unhexrgb("#FFD700"), | |
"level 3 text": unhexrgb("#5F5FFF"), | |
"level 3 background": unhexrgb("#FFFF00"), | |
} | |
) | |
print("----- blue") | |
display( | |
{ | |
"central color": unhexrgb("#5F5FFF"), | |
"selected window bg": unhexrgb("#FFD700"), | |
"hidden window text": unhexrgb("#FFAF00"), | |
"level 1 text": unhexrgb("#FFD700"), | |
"level 1 background": unhexrgb("#5F87FF"), | |
"level 2 text": unhexrgb("#FFFFAF"), | |
"level 2 background": unhexrgb("#5FD7FF"), | |
"level 3 text": unhexrgb("#5F5FFF"), | |
"level 3 background": unhexrgb("#5FFFFF"), | |
} | |
) | |
print("----- blue2") | |
display( | |
{ | |
"central color": unhexrgb("#2D4BC5"), | |
"selected window bg": unhexrgb("#FFE700"), | |
"hidden window text": unhexrgb("#FFC952"), | |
"level 1 text": unhexrgb("#FFDA24"), | |
"level 1 background": unhexrgb("#5B70FF"), | |
"level 2 text": unhexrgb("#FFEE99"), | |
"level 2 background": unhexrgb("#00B2FF"), | |
"level 3 text": unhexrgb("#5F5FFF"), | |
"level 3 background": unhexrgb("#24F2FF"), | |
} | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment