Add conversion to/from the CIE-L*ab color space.

This commit is contained in:
Rodrigo Girão Serrão
2022-04-04 15:37:23 -04:00
parent ca77f7e24d
commit 90bdf0e778
2 changed files with 67 additions and 1 deletions

View File

@@ -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

View File

@@ -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)