混合 RGB 颜色 (L*a*b*)

发布于 2024-08-31 05:00:06 字数 399 浏览 8 评论 0原文

基本上我想要两种混合两种颜色color1color2。 由于简单的计算会带来像蓝色+黄色=灰色((color1.r + color2.r)/2等)这样的东西,我做了一些研究,发现显然混合颜色是为了使混合颜色看起来我们也期望它(例如蓝色+黄色=绿色)并不是那么简单。

另一篇 stackoverflow 帖子告诉我的是,为了正确实现两者的混合,我必须使用 Lab* 空间 / CIELAB 并链接到有关该主题的维基百科页面。

我发现它内容丰富,但我无法真正理解如何将 RGB 转换为(sRGB 和)Lab* - 如何混合获得的颜色以及如何转换回来

我希望这里有人可以帮助我

,谢谢,

塞缪尔

Basically I want two mix two colours color1 and color2.
Since simple calculation's bring up stuff like blue+yellow = grey ((color1.r + color2.r)/2 etc) i did some research and found that apparently mixing colors in order for the mixed color to look like we expect it too (e.g. blue+yellow = green) isn't that straight forward.

What another stackoverflow post taught me was that in order two achieve the mixture correctly i'd have to use the Lab* space / CIELAB and linked to the wikipedia page about this topic.

I found it informative but i couldn't really understand how to convert RGB to (sRGB and than to) Lab* - how to mix the obtained colors and how to convert back

I hope somebody here can help me

Thanks,

Samuel

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

巷子口的你 2024-09-07 05:00:07

要在 LAB 颜色空间中的两种 RGB 颜色之间进行插值,首先需要通过 XYZ 将每种颜色转换为 LAB(RGB -> XYZ -> LAB)。

function RGBtoXYZ([R, G, B]) {
    const [var_R, var_G, var_B] = [R, G, B]
        .map(x => x / 255)
        .map(x => x > 0.04045
            ? Math.pow(((x + 0.055) / 1.055), 2.4)
            : x / 12.92)
        .map(x => x * 100)

    // Observer. = 2°, Illuminant = D65
    X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
    Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
    Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
    return [X, Y, Z]
}

function XYZtoRGB([X, Y, Z]) {
    //X, Y and Z input refer to a D65/2° standard illuminant.
    //sR, sG and sB (standard RGB) output range = 0 ÷ 255

    let var_X = X / 100
    let var_Y = Y / 100
    let var_Z = Z / 100

    var_R = var_X *  3.2406 + var_Y * -1.5372 + var_Z * -0.4986
    var_G = var_X * -0.9689 + var_Y *  1.8758 + var_Z *  0.0415
    var_B = var_X *  0.0557 + var_Y * -0.2040 + var_Z *  1.0570

    return [var_R, var_G, var_B]
        .map(n => n > 0.0031308
            ? 1.055 * Math.pow(n, (1 / 2.4)) - 0.055
            : 12.92 * n)
        .map(n => n * 255)
}

您可以使用此函数在两种 LAB 颜色之间进行插值

const sum = (a, b) => a.map((_, i) => a[i] + b[i])

const interpolate = (a, b, p) => {
    return sum(
        a.map(x => x * p),
        b.map(x => x * (1 - p)),
    )
}

const colour1 = [...]
const colour2 = [...]
interpolate(colour1, colour2, 0.2) // take 20% of colour1 and 80% of colour2

最后,将结果从 LAB 转换回 RGB

function XYZtoLAB([x, y, z]) {
    const [ var_X, var_Y, var_Z ] = [ x / ref_X, y / ref_Y, z / ref_Z ]
        .map(a => a > 0.008856
            ? Math.pow(a, 1 / 3)
            : (7.787 * a) + (16 / 116))

    CIE_L = (116 * var_Y) - 16
    CIE_a = 500 * (var_X - var_Y)
    CIE_b = 200 * (var_Y - var_Z)

    return [CIE_L, CIE_a, CIE_b]
}

function LABtoXYZ([l, a, b]) {

    const var_Y = (l + 16) / 116
    const var_X = a / 500 + var_Y
    const var_Z = var_Y - b / 200

    const [X, Y, Z] = [var_X, var_Y, var_Z]
        .map(n => Math.pow(n, 3) > 0.008856
            ? Math.pow(n, 3)
            : (n - 16 / 116) / 7.787)

    return [X * ref_X, Y * ref_Y, Z * ref_Z]
}

其他一些有用的函数

const parseRGB = s => s.substring(s.indexOf('(') + 1, s.length - 1).split(',')
    .map(ss => parseInt(ss.trim()))

const RGBtoString = ([r, g, b]) => `rgb(${r}, ${g}, ${b})`

总而言之:

document.body.style.backgroundColor = RGBtoString(XYZtoRGB(LABtoXYZ(interpolate(
    XYZtoLAB(RGBtoXYZ(parseRGB('rgb(255, 0, 0)'))),
    XYZtoLAB(RGBtoXYZ(parseRGB('rgb(0, 255, 0)'))),
    0.2,
))))

颜色空间转换参考:http://www.easyrgb.com/en/math.php

To interpolate between two RGB colours in the LAB colour space, you first need to convert each colour to LAB via XYZ (RGB -> XYZ -> LAB).

function RGBtoXYZ([R, G, B]) {
    const [var_R, var_G, var_B] = [R, G, B]
        .map(x => x / 255)
        .map(x => x > 0.04045
            ? Math.pow(((x + 0.055) / 1.055), 2.4)
            : x / 12.92)
        .map(x => x * 100)

    // Observer. = 2°, Illuminant = D65
    X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
    Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
    Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
    return [X, Y, Z]
}

function XYZtoRGB([X, Y, Z]) {
    //X, Y and Z input refer to a D65/2° standard illuminant.
    //sR, sG and sB (standard RGB) output range = 0 ÷ 255

    let var_X = X / 100
    let var_Y = Y / 100
    let var_Z = Z / 100

    var_R = var_X *  3.2406 + var_Y * -1.5372 + var_Z * -0.4986
    var_G = var_X * -0.9689 + var_Y *  1.8758 + var_Z *  0.0415
    var_B = var_X *  0.0557 + var_Y * -0.2040 + var_Z *  1.0570

    return [var_R, var_G, var_B]
        .map(n => n > 0.0031308
            ? 1.055 * Math.pow(n, (1 / 2.4)) - 0.055
            : 12.92 * n)
        .map(n => n * 255)
}

You may use this function to interpolate between two LAB colours

const sum = (a, b) => a.map((_, i) => a[i] + b[i])

const interpolate = (a, b, p) => {
    return sum(
        a.map(x => x * p),
        b.map(x => x * (1 - p)),
    )
}

const colour1 = [...]
const colour2 = [...]
interpolate(colour1, colour2, 0.2) // take 20% of colour1 and 80% of colour2

Finally, convert the result back from LAB to RGB

function XYZtoLAB([x, y, z]) {
    const [ var_X, var_Y, var_Z ] = [ x / ref_X, y / ref_Y, z / ref_Z ]
        .map(a => a > 0.008856
            ? Math.pow(a, 1 / 3)
            : (7.787 * a) + (16 / 116))

    CIE_L = (116 * var_Y) - 16
    CIE_a = 500 * (var_X - var_Y)
    CIE_b = 200 * (var_Y - var_Z)

    return [CIE_L, CIE_a, CIE_b]
}

function LABtoXYZ([l, a, b]) {

    const var_Y = (l + 16) / 116
    const var_X = a / 500 + var_Y
    const var_Z = var_Y - b / 200

    const [X, Y, Z] = [var_X, var_Y, var_Z]
        .map(n => Math.pow(n, 3) > 0.008856
            ? Math.pow(n, 3)
            : (n - 16 / 116) / 7.787)

    return [X * ref_X, Y * ref_Y, Z * ref_Z]
}

Some other helpful functions

const parseRGB = s => s.substring(s.indexOf('(') + 1, s.length - 1).split(',')
    .map(ss => parseInt(ss.trim()))

const RGBtoString = ([r, g, b]) => `rgb(${r}, ${g}, ${b})`

All together:

document.body.style.backgroundColor = RGBtoString(XYZtoRGB(LABtoXYZ(interpolate(
    XYZtoLAB(RGBtoXYZ(parseRGB('rgb(255, 0, 0)'))),
    XYZtoLAB(RGBtoXYZ(parseRGB('rgb(0, 255, 0)'))),
    0.2,
))))

Reference for colour space conversions: http://www.easyrgb.com/en/math.php

御弟哥哥 2024-09-07 05:00:06

1)将sRGB转换为RGB。来自 GEGL:

  

static inline double
linear_to_gamma_2_2 (double value)
{
  if (value > 0.0030402477F)
    return 1.055F * pow (value, (1.0F/2.4F)) - 0.055F;
  return 12.92F * value;
}


static inline double
gamma_2_2_to_linear (double value)
{
  if (value > 0.03928F)
    return pow ((value + 0.055F) / 1.055F, 2.4F);
  return value / 12.92F;
}

2) RGB 到 CIELAB。查看 OpenCV 源代码 [/src/cv/cvcolor.cpp]。有颜色空间转换的函数 [icvBGRx2Lab_32f_CnC3R]

3) 混合颜色通道。

4)将所有颜色转换回来。

1) convert sRGB to RGB. From GEGL:

  

static inline double
linear_to_gamma_2_2 (double value)
{
  if (value > 0.0030402477F)
    return 1.055F * pow (value, (1.0F/2.4F)) - 0.055F;
  return 12.92F * value;
}


static inline double
gamma_2_2_to_linear (double value)
{
  if (value > 0.03928F)
    return pow ((value + 0.055F) / 1.055F, 2.4F);
  return value / 12.92F;
}

2) RGB to CIELAB. Look in OpenCV source [/src/cv/cvcolor.cpp]. There are functions for color space conversions [icvBGRx2Lab_32f_CnC3R]

3) mix color channels.

4) make all the color conversions back.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文