Color.Conversion.Lindbloom (Color v0.4.0)

Copy Markdown

Color space conversion functions based on the formulas published by Bruce Lindbloom at http://www.brucelindbloom.com/index.html?Math.html.

All functions operate on plain tuples/lists of floats so they can be composed freely. Reference whites (wr) are supplied as {xr, yr, zr} tuples in the same scale as the XYZ values (typically Y = 1.0 or Y = 100.0; both work as long as the inputs are consistent).

The CIE constants ε and κ are used in their exact rational form as recommended by Lindbloom, rather than the rounded values found in many older references.

Constants

  • ε = 216/24389 ≈ 0.008856.

  • κ = 24389/27 ≈ 903.2963.

Summary

Functions

Returns the CIE constants ε and κ used by the conversions.

Applies simple gamma companding (linear → non-linear).

Inverts simple gamma companding (non-linear → linear).

Applies the Hybrid Log-Gamma inverse EOTF (linear scene-referred light → HLG signal).

Applies the HLG EOTF (HLG signal → linear scene-referred light).

Inverts a 3x3 matrix represented as a list of rows. Used to derive the XYZ→RGB matrix from the RGB→XYZ matrix.

Applies the L companding function (linear → L).

Inverts the L companding function (L → linear).

Converts CIE L*a*b* to cylindrical LCHab.

Converts CIE L*a*b* to a CIE XYZ triple.

Converts cylindrical LCHab to CIE L*a*b*.

Converts cylindrical LCHuv to CIE L*u*v*.

Converts CIE L*u*v* to cylindrical LCHuv.

Converts CIE L*u*v* to a CIE XYZ triple.

Multiplies two 3x3 matrices given as lists of rows.

Applies the SMPTE ST 2084 / BT.2100 PQ inverse EOTF (linear luminance → non-linear PQ signal).

Applies the SMPTE ST 2084 / BT.2100 PQ EOTF (PQ signal → linear luminance in [0, 1] where 1.0 = 10,000 cd/m²).

Applies the ITU-R BT.709 opto-electronic transfer function (linear → non-linear).

Inverts the BT.709 OETF (non-linear → linear).

Applies the ITU-R BT.2020 OETF at 12-bit precision (linear → non-linear).

Inverts the BT.2020 12-bit OETF (non-linear → linear).

Converts linear RGB to XYZ using a working-space matrix m.

Applies the sRGB companding function (linear → sRGB).

Inverts the sRGB companding function (sRGB → linear).

Computes the 3x3 RGB→XYZ matrix for an RGB working space from its primary chromaticities and reference white, per Lindbloom.

Converts xyY chromaticity coordinates to a CIE XYZ triple.

Converts a CIE XYZ triple to CIE L*a*b*.

Converts a CIE XYZ triple to CIE L*u*v*.

Converts XYZ to linear RGB using the inverse working-space matrix mi.

Converts a CIE XYZ triple to xyY.

Functions

constants()

Returns the CIE constants ε and κ used by the conversions.

Returns

  • A {epsilon, kappa} tuple of floats.

Examples

iex> {e, k} = Color.Conversion.Lindbloom.constants()
iex> Float.round(e, 6)
0.008856
iex> Float.round(k, 4)
903.2963

gamma_compand(v, gamma)

Applies simple gamma companding (linear → non-linear).

Arguments

  • v is a linear channel value.

  • gamma is the gamma exponent (for example 2.2).

gamma_inverse_compand(v, gamma)

Inverts simple gamma companding (non-linear → linear).

Arguments

  • v is a companded channel value.

  • gamma is the gamma exponent.

hlg_compand(v)

Applies the Hybrid Log-Gamma inverse EOTF (linear scene-referred light → HLG signal).

Input and output are both in [0, 1]. The HLG curve is gamma at the dark end and logarithmic at the bright end, meeting at E = 1/12.

Arguments

  • v is a linear channel value in [0, 1].

hlg_inverse_compand(v)

Applies the HLG EOTF (HLG signal → linear scene-referred light).

Arguments

  • v is an HLG-encoded channel value in [0, 1].

invert3(list)

Inverts a 3x3 matrix represented as a list of rows. Used to derive the XYZ→RGB matrix from the RGB→XYZ matrix.

Arguments

  • m is a 3x3 matrix as a list of three three-element rows.

Returns

  • The inverse matrix in the same shape.

l_star_compand(v)

Applies the L companding function (linear → L).

Arguments

  • v is a linear channel value in [0, 1].

l_star_inverse_compand(v)

Inverts the L companding function (L → linear).

Arguments

  • v is an L*-companded channel value.

lab_to_lchab(arg)

Converts CIE L*a*b* to cylindrical LCHab.

Arguments

  • lab is an {l, a, b} tuple.

Returns

  • An {l, c, h} tuple where h is in degrees in the range [0, 360).

Examples

iex> Color.Conversion.Lindbloom.lab_to_lchab({50.0, 0.0, 0.0})
{50.0, 0.0, 0.0}

lab_to_xyz(arg1, arg2)

Converts CIE L*a*b* to a CIE XYZ triple.

Arguments

  • lab is an {l, a, b} tuple.

  • wr is the {Xr, Yr, Zr} reference white tuple.

Returns

  • An {X, Y, Z} tuple.

Examples

iex> {x, y, z} = Color.Conversion.Lindbloom.lab_to_xyz({100.0, 0.0, 0.0}, {0.95047, 1.0, 1.08883})
iex> {Float.round(x, 5), Float.round(y, 5), Float.round(z, 5)}
{0.95047, 1.0, 1.08883}

lchab_to_lab(arg)

Converts cylindrical LCHab to CIE L*a*b*.

Arguments

  • lch is an {l, c, h} tuple with h in degrees.

Returns

  • An {l, a, b} tuple.

Examples

iex> Color.Conversion.Lindbloom.lchab_to_lab({50.0, 0.0, 0.0})
{50.0, 0.0, 0.0}

lchuv_to_luv(arg)

Converts cylindrical LCHuv to CIE L*u*v*.

Arguments

  • lch is an {l, c, h} tuple with h in degrees.

Returns

  • An {l, u, v} tuple.

Examples

iex> Color.Conversion.Lindbloom.lchuv_to_luv({50.0, 0.0, 0.0})
{50.0, 0.0, 0.0}

luv_to_lchuv(arg)

Converts CIE L*u*v* to cylindrical LCHuv.

Arguments

  • luv is an {l, u, v} tuple.

Returns

  • An {l, c, h} tuple where h is in degrees in the range [0, 360).

Examples

iex> Color.Conversion.Lindbloom.luv_to_lchuv({50.0, 0.0, 0.0})
{50.0, 0.0, 0.0}

luv_to_xyz(arg1, arg2)

Converts CIE L*u*v* to a CIE XYZ triple.

Arguments

  • luv is an {l, u, v} tuple.

  • wr is the {Xr, Yr, Zr} reference white tuple.

Returns

  • An {X, Y, Z} tuple.

Examples

iex> Color.Conversion.Lindbloom.luv_to_xyz({0.0, 0.0, 0.0}, {0.95047, 1.0, 1.08883})
{0.0, 0.0, 0.0}

matmul3(list1, list2)

Multiplies two 3x3 matrices given as lists of rows.

Arguments

  • a is a 3x3 matrix as a list of three three-element rows.

  • b is a 3x3 matrix in the same shape.

Returns

  • The product a · b in the same shape.

pq_compand(v)

Applies the SMPTE ST 2084 / BT.2100 PQ inverse EOTF (linear luminance → non-linear PQ signal).

Input is absolute luminance in [0, 1] where 1.0 represents 10,000 cd/m². Output is the PQ-encoded signal in [0, 1].

Arguments

  • v is a linear luminance value in [0, 1].

pq_inverse_compand(v)

Applies the SMPTE ST 2084 / BT.2100 PQ EOTF (PQ signal → linear luminance in [0, 1] where 1.0 = 10,000 cd/m²).

Arguments

  • v is a PQ-encoded value in [0, 1].

rec709_compand(v)

Applies the ITU-R BT.709 opto-electronic transfer function (linear → non-linear).

BT.709 uses the same shape as the Rec. 2020 SDR curve but is defined at 8/10-bit precision; this implementation matches both.

Arguments

  • v is a linear channel value in [0, 1].

rec709_inverse_compand(v)

Inverts the BT.709 OETF (non-linear → linear).

Arguments

  • v is a non-linear BT.709 channel value in [0, 1].

rec2020_compand(v)

Applies the ITU-R BT.2020 OETF at 12-bit precision (linear → non-linear).

BT.2020 refines the BT.709 curve with higher-precision constants for 12-bit systems. At 10-bit precision BT.2020 is identical to BT.709.

Arguments

  • v is a linear channel value in [0, 1].

rec2020_inverse_compand(v)

Inverts the BT.2020 12-bit OETF (non-linear → linear).

Arguments

  • v is a non-linear BT.2020 channel value in [0, 1].

rgb_to_xyz(arg, list)

Converts linear RGB to XYZ using a working-space matrix m.

Uses plain f64 arithmetic rather than Nx: for single-color 3x3 work the BEAM's float ops are roughly 25x faster than Nx on the host backend, and preserve double precision. Nx is still the right choice for batched color operations elsewhere in this project.

Arguments

  • rgb is an {r, g, b} tuple of linear (companding-removed) values.

  • m is the 3x3 RGB→XYZ matrix for the working space, as a list of three three-element rows.

Returns

  • An {X, Y, Z} tuple.

Examples

iex> m = [[0.4124564, 0.3575761, 0.1804375],
...>      [0.2126729, 0.7151522, 0.0721750],
...>      [0.0193339, 0.1191920, 0.9503041]]
iex> {x, y, z} = Color.Conversion.Lindbloom.rgb_to_xyz({1.0, 1.0, 1.0}, m)
iex> {Float.round(x, 4), Float.round(y, 4), Float.round(z, 4)}
{0.9505, 1.0, 1.0888}

srgb_compand(v)

Applies the sRGB companding function (linear → sRGB).

Arguments

  • v is a linear channel value in [0, 1].

srgb_inverse_compand(v)

Inverts the sRGB companding function (sRGB → linear).

Arguments

  • v is a companded sRGB channel value in [0, 1].

working_space_matrix(arg1, arg2)

Computes the 3x3 RGB→XYZ matrix for an RGB working space from its primary chromaticities and reference white, per Lindbloom.

Given the primaries {xr, yr}, {xg, yg}, {xb, yb} and the reference white {Xw, Yw, Zw} the matrix is [Sr·Xr Sg·Xg Sb·Xb; …] where the scale factors S are found by solving M · [Sr Sg Sb]ᵀ = W.

Arguments

  • primaries is a {{xr, yr}, {xg, yg}, {xb, yb}} tuple of chromaticities.

  • wr is the {Xw, Yw, Zw} reference white.

Returns

  • The RGB→XYZ matrix as a list of three three-element rows.

xyy_to_xyz(arg)

Converts xyY chromaticity coordinates to a CIE XYZ triple.

Arguments

  • xyy is an {x, y, y_big} tuple.

Returns

  • An {X, Y, Z} tuple.

Examples

iex> {x, y, z} = Color.Conversion.Lindbloom.xyy_to_xyz({0.3127, 0.3290, 1.0})
iex> {Float.round(x, 5), Float.round(y, 5), Float.round(z, 5)}
{0.95046, 1.0, 1.08906}

xyz_to_lab(arg1, arg2)

Converts a CIE XYZ triple to CIE L*a*b*.

Arguments

  • xyz is an {X, Y, Z} tuple.

  • wr is the {Xr, Yr, Zr} reference white tuple.

Returns

  • An {l, a, b} tuple.

Examples

iex> Color.Conversion.Lindbloom.xyz_to_lab({0.95047, 1.0, 1.08883}, {0.95047, 1.0, 1.08883})
{100.0, 0.0, 0.0}

xyz_to_luv(arg1, arg2)

Converts a CIE XYZ triple to CIE L*u*v*.

Arguments

  • xyz is an {X, Y, Z} tuple.

  • wr is the {Xr, Yr, Zr} reference white tuple.

Returns

  • An {l, u, v} tuple.

Examples

iex> Color.Conversion.Lindbloom.xyz_to_luv({0.0, 0.0, 0.0}, {0.95047, 1.0, 1.08883})
{0.0, 0.0, 0.0}

xyz_to_rgb(arg, list)

Converts XYZ to linear RGB using the inverse working-space matrix mi.

Arguments

  • xyz is an {X, Y, Z} tuple.

  • mi is the 3x3 XYZ→RGB (inverse) matrix, as a list of three three-element rows.

Returns

  • An {r, g, b} tuple of linear values.

xyz_to_xyy(arg1, arg2)

Converts a CIE XYZ triple to xyY.

When X + Y + Z = 0 the chromaticity is taken from the reference white as prescribed by Lindbloom.

Arguments

  • xyz is an {x, y, z} tuple.

  • wr is the {xr, yr, zr} reference white used when X + Y + Z = 0.

Returns

  • An {x, y, y_big} tuple where x and y are chromaticity coordinates and y_big is the original Y.

Examples

iex> Color.Conversion.Lindbloom.xyz_to_xyy({0.5, 0.5, 0.5}, {0.95047, 1.0, 1.08883})
{0.3333333333333333, 0.3333333333333333, 0.5}

iex> {x, y, yy} = Color.Conversion.Lindbloom.xyz_to_xyy({0.95047, 1.0, 1.08883}, {0.95047, 1.0, 1.08883})
iex> {Float.round(x, 5), Float.round(y, 5), Float.round(yy, 4)}
{0.31273, 0.32902, 1.0}