mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add conversion to/from the CIE-L*ab color space.
This commit is contained in:
@@ -33,6 +33,14 @@ class HSV(NamedTuple):
|
||||
v: float
|
||||
|
||||
|
||||
class Lab(NamedTuple):
|
||||
"""A color in CIE-L*ab format."""
|
||||
|
||||
L: float
|
||||
a: float
|
||||
b: float
|
||||
|
||||
|
||||
RE_COLOR = re.compile(
|
||||
r"""^
|
||||
\#([0-9a-fA-F]{6})$|
|
||||
@@ -367,6 +375,55 @@ class ColorPair(NamedTuple):
|
||||
)
|
||||
|
||||
|
||||
def rgb_to_lab(rgb: Color) -> Lab:
|
||||
"""Convert an RGB color to the CIE-L*ab format.
|
||||
|
||||
See https://stackoverflow.com/a/8433985/2828287."""
|
||||
|
||||
r, g, b = rgb.r / 255, rgb.g / 255, rgb.b / 255
|
||||
|
||||
r = pow((r + 0.055) / 1.055, 2.4) if r > 0.04045 else r / 12.92
|
||||
g = pow((g + 0.055) / 1.055, 2.4) if g > 0.04045 else g / 12.92
|
||||
b = pow((b + 0.055) / 1.055, 2.4) if b > 0.04045 else b / 12.92
|
||||
|
||||
x = (r * 41.24 + g * 35.76 + b * 18.05) / 95.047
|
||||
y = (r * 21.26 + g * 71.52 + b * 7.22) / 100
|
||||
z = (r * 1.93 + g * 11.92 + b * 95.05) / 108.883
|
||||
|
||||
off = 16 / 116
|
||||
x = pow(x, 1 / 3) if x > 0.008856 else 7.787 * x + off
|
||||
y = pow(y, 1 / 3) if y > 0.008856 else 7.787 * y + off
|
||||
z = pow(z, 1 / 3) if z > 0.008856 else 7.787 * z + off
|
||||
|
||||
return Lab(116 * y - 16, 500 * (x - y), 200 * (y - z))
|
||||
|
||||
|
||||
def lab_to_rgb(lab: Lab) -> Color:
|
||||
"""Convert a CIE-L*ab color to RGB.
|
||||
|
||||
See https://stackoverflow.com/a/8433985/2828287
|
||||
"""
|
||||
|
||||
y = (lab.L + 16) / 116
|
||||
x = lab.a / 500 + y
|
||||
z = y - lab.b / 200
|
||||
|
||||
off = 16 / 116
|
||||
y = pow(y, 3) if y > 0.2068930344 else (y - off) / 7.787
|
||||
x = 0.95047 * pow(x, 3) if x > 0.2068930344 else 0.122059 * (x - off)
|
||||
z = 1.08883 * pow(z, 3) if z > 0.2068930344 else 0.139827 * (z - off)
|
||||
|
||||
r = x * 3.2406 + y * -1.5372 + z * -0.4986
|
||||
g = x * -0.9689 + y * 1.8758 + z * 0.0415
|
||||
b = x * 0.0557 + y * -0.2040 + z * 1.0570
|
||||
|
||||
r = 1.055 * pow(r, 1 / 2.4) - 0.055 if r > 0.0031308 else 12.92 * r
|
||||
g = 1.055 * pow(g, 1 / 2.4) - 0.055 if g > 0.0031308 else 12.92 * g
|
||||
b = 1.055 * pow(b, 1 / 2.4) - 0.055 if b > 0.0031308 else 12.92 * b
|
||||
|
||||
return Color(int(r * 255), int(g * 255), int(b * 255))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
from rich import print
|
||||
|
||||
@@ -3,7 +3,7 @@ import pytest
|
||||
from rich.color import Color as RichColor
|
||||
from rich.text import Text
|
||||
|
||||
from textual.color import Color, ColorPair
|
||||
from textual.color import Color, ColorPair, Lab, rgb_to_lab
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -76,3 +76,12 @@ def test_hls():
|
||||
assert red.hls == pytest.approx(
|
||||
(0.9888888888888889, 0.43137254901960786, 0.818181818181818)
|
||||
)
|
||||
|
||||
|
||||
def test_rgb_to_lab():
|
||||
r, g, b = 10, 23, 73
|
||||
rgb = Color(r, g, b)
|
||||
lab = rgb_to_lab(rgb)
|
||||
assert lab.L == pytest.approx(10.245)
|
||||
assert lab.a == pytest.approx(15.913)
|
||||
assert lab.b == pytest.approx(-32.672)
|
||||
|
||||
Reference in New Issue
Block a user