开发者

Mix RGB colors (L*a*b*)

开发者 https://www.devze.com 2022-12-28 23:51 出处:网络
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 m

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 fo开发者_StackOverflow中文版r 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


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号