Skip to content

Instantly share code, notes, and snippets.

@lboulard
Last active November 20, 2022 18:44
Show Gist options
  • Save lboulard/df1629f2e27034b123d67f5268aa1b3e to your computer and use it in GitHub Desktop.
Save lboulard/df1629f2e27034b123d67f5268aa1b3e to your computer and use it in GitHub Desktop.
RGB to xterm/oklch
ipython
matplotlib
black
isort
#
# 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
colour-science
numpy
#
# 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
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