【问题标题】:Converting RGB to RGBW将 RGB 转换为 RGBW
【发布时间】:2017-03-11 18:25:09
【问题描述】:

我知道这个has already been asked,但那里给出的anwser 不起作用。我花了一个多小时寻找公式或算法,但一无所获。因此,我开始编写自己的算法,以最有效的方式将 RGB 转换为 RGBW。这是我目前得到的:

        //'Ri', 'Gi', and 'Bi' correspond to the Red, Green, and Blue inputs.
        var M = Math.Max(Ri, Math.Max(Gi, Bi)); //The maximum value between R,G, and B.
        int Wo =0; //White output
        int Ro=0; //Red output
        int Go=0; //Green output
        int Bo=0; //Blue output

        int av = 0; //Average between the two minimum values
        int hR = 0; //Red with 100% hue
        int hG = 0; //Green with 100% hue
        int hB = 0; //Blue with 100% hue

        //These 4 lines serve to figure out what the input color is with 100% hue.
        float multiplier = 255.0f / M;
        hR = Convert.ToInt32(Ri * multiplier);
        hG = Convert.ToInt32(Gi * multiplier);
        hB = Convert.ToInt32(Bi * multiplier);

        //Depending on the maximum value, get an average of the least used colors, weighted for their importance in the overall hue.
        //This is the problematic part
        if (M == Ri)
           av = (Bi*hB + Gi*hG) / (hB+hG);
        else if (M == Gi)
            av = (Ri*hR + Bi*hB) / (hR+hB);
        else if (M == Bi)
            av = (Gi*hG + Ri*hR) / (hG+hR);

        //Set the rgbw colors
        Wo = av;
        Bo = Bi - av;
        Ro = Ri - av;
        Go = Gi - av;
        if (Wo < 1) Wo = 0;
        if (Bo < 1) Bo = 0;
        if (Ro < 1) Ro = 0;
        if (Go < 1) Go = 0;
        if (Wo > 255) Wo = 255;
        if (Bo > 255) Bo = 255;
        if (Ro > 255) Ro = 255;
        if (Go > 255) Go = 255;

如果我正在处理的颜色是原色,它可以正常工作,但在任何其他情况下都不会。是什么让它无处不在?我是否走在正确的轨道上?

编辑:这是我遇到的问题的 .gif。 RGBW 值一直在底部

【问题讨论】:

  • 我想知道您的整数除法是否是问题的一部分。您不希望倍数为浮点数/小数并截断/舍入最终值吗?
  • “不起作用”是什么意思?
  • @shawnt00 我不这么认为。我很确定 .NET 可以解决这个问题。通过除以双精度数或浮点数然后转换为整数得到相同的结果,但我得到相同的结果。
  • @Paul Abbott 我已经用 gif 更新了帖子,显示了这个问题。
  • int multiplier = 255 / M; - 这真的是整数除法吗?

标签: c# colors rgb color-management color-theory


【解决方案1】:

终于搞明白怎么把RGB转RGBW了,原来我之前的方法完全错了:

//Get the maximum between R, G, and B
float tM = Math.Max(Ri, Math.Max(Gi, Bi));

//If the maximum value is 0, immediately return pure black.
if(tM == 0)
   { return new rgbwcolor() { r = 0, g = 0, b = 0, w = 0 }; }

//This section serves to figure out what the color with 100% hue is
float multiplier = 255.0f / tM;
float hR = Ri * multiplier;
float hG = Gi * multiplier;
float hB = Bi * multiplier;  

//This calculates the Whiteness (not strictly speaking Luminance) of the color
float M = Math.Max(hR, Math.Max(hG, hB));
float m = Math.Min(hR, Math.Min(hG, hB));
float Luminance = ((M + m) / 2.0f - 127.5f) * (255.0f/127.5f) / multiplier;

//Calculate the output values
int Wo = Convert.ToInt32(Luminance);
int Bo = Convert.ToInt32(Bi - Luminance);
int Ro = Convert.ToInt32(Ri - Luminance);
int Go = Convert.ToInt32(Gi - Luminance);

//Trim them so that they are all between 0 and 255
if (Wo < 0) Wo = 0;
if (Bo < 0) Bo = 0;
if (Ro < 0) Ro = 0;
if (Go < 0) Go = 0;
if (Wo > 255) Wo = 255;
if (Bo > 255) Bo = 255;
if (Ro > 255) Ro = 255;
if (Go > 255) Go = 255;
return new rgbwcolor() { r = Ro, g = Go, b = Bo, w = Wo };

欢迎任何优化想法:)

【讨论】:

  • 这个解决方案给出了与直接转换完全相同的结果: Wo = Min(Ri, Gi, Bi); Ro = Ri - Wo;围棋 = Gi - Wo; Bo = Bi - Wo;我在这里错过了什么吗?
【解决方案2】:

我制定了一个算法,该算法考虑了您的“白色”LED 的色温(因为这可能因灯带而异 - 我的是暖白色,即 4500k)。要点是here,代码如下,更多上下文的博文是Ouch! My Eyes! Arduino WS2812B Setup & RGBW Transformation (Cabinet Light Pt. 3)

// Reference, currently set to 4500k white light:
// https://andi-siess.de/rgb-to-color-temperature/
const uint8_t kWhiteRedChannel = 255;
const uint8_t kWhiteGreenChannel = 219;
const uint8_t kWhiteBlueChannel = 186;

// The transformation has to be normalized to 255
static_assert(kWhiteRedChannel >= 255 ||
              kWhiteGreenChannel >= 255 ||
              kWhiteBlueChannel >= 255);

CRGBW GetRgbwFromRgb2(CRGB rgb) {
  uint8_t r = rgb.r;
  uint8_t g = rgb.g;
  uint8_t b = rgb.b;

  // These values are what the 'white' value would need to
  // be to get the corresponding color value.
  double whiteValueForRed = r * 255.0 / kWhiteRedChannel;
  double whiteValueForGreen = g * 255.0 / kWhiteGreenChannel;
  double whiteValueForBlue = b * 255.0 / kWhiteBlueChannel;

  // Set the white value to the highest it can be for the given color
  // (without over saturating any channel - thus the minimum of them).
  double minWhiteValue = min(whiteValueForRed,
                             min(whiteValueForGreen,
                                 whiteValueForBlue));
  uint8_t Wo = (minWhiteValue <= 255 ? (uint8_t) minWhiteValue : 255);

  // The rest of the channels will just be the original value minus the
  // contribution by the white channel.
  uint8_t Ro = (uint8_t)(r - minWhiteValue * kWhiteRedChannel / 255);
  uint8_t Go = (uint8_t)(g - minWhiteValue * kWhiteGreenChannel / 255);
  uint8_t Bo = (uint8_t)(b - minWhiteValue * kWhiteBlueChannel / 255);

  return CRGBW(Ro, Go, Bo, Wo);
}

【讨论】:

    【解决方案3】:

    我认为问题出在这里:

        if (M == Ri)
           av = (Bi*hB + Gi*hG) / (hB+hG);
        else if (M == Gi)
            av = (Ri*hR + Bi*hB) / (hR+hB);
        else if (M == Bi)
            av = (Gi*hG + Ri*hR) / (hG+hR);
    

    你在这里做整数除法。您所有的变量都是整数,因此除法将是整数除法。

        if (M == Ri)
           av = (int)((float)(Bi*hB + Gi*hG) / (hB+hG));
        else if (M == Gi)
            av = (int)((float)(Ri*hR + Bi*hB) / (hR+hB));
        else if (M == Bi)
            av = (int)((float)(Gi*hG + Ri*hR) / (hG+hR));
    

    这将进行浮点除法,并应为您提供所需的答案。您仍然可能会发现舍入错误 - 在这种情况下将 float 更改为 double

    将乘数计算改为浮点数:

    float multiplier = 255.0 / M;
    

    只解决了一半的问题,但现在在您的测试中导致另一个复杂问题:

    if (M == Ri)
    else if (M == Gi)
    else if (M == Bi)
    

    现在不太可能是真的。您需要在测试中添加舍入误差因子。

    if (Math.Abs(M - Ri) < epsilon)
    else if (Math.Abs(M - Gi) < epsilon)
    else if (Math.Abs(M - Bi) < epsilon)
    

    epsilon 是一个合适的小值(通常我建议 10e-6,但您可以进行实验。

    此外,如果 M 与您未设置的任何 RGB 值都不够接近 av - 它始终保持设置为零。这也将为您提供您所看到的一切都是黑色的结果

    【讨论】:

    • 已修复,但仍然一无所获。我很确定问题来自我使用的实际逻辑。如果你看看当我尝试使绿松石饱和/去饱和时会发生什么,我得到的白色输出为 255,而它应该为 0(完全饱和的绿松石中没有白色)。当我使蓝色饱和/去饱和时,它应该表现得像。
    【解决方案4】:

    我采取了不同的方法。我只是检查最低的值并将其设为白色通道,然后从红色、绿色和蓝色通道中减去该值。这有点简化,但效果很好并且易于实现。 128,128,0 变为 128,128,0,0 64,64,128 变为 0,0,64,64 等。 这也允许轻松实现颜色校正。当您构建 rgbw 值时,说蓝色有点强(典型)将蓝色通道限制为 250 而不是 255 之类的较低值。我不能说这在 C 或 C++ 中运行良好,但在 ADA 中运行良好。

    【讨论】:

      【解决方案5】:

      这个 repo 可以为你完成这项工作https://github.com/iamh2o/rgbw_colorspace_converter/

      我和朋友一起编写了这个模块,“颜色”对象可以通过多个颜色系统实例化,并且该对象可以输出到它支持的所有其他颜色系统的翻译——经过大量研究(a关键是https://www.neltnerlabs.com/saikoled/how-to-convert-from-hsi-to-rgb-white),我们终于搞定了[HSI/HSL/HSV/RGB/HEX] -> RGBW转换。

      有大量的软件包解决了一般色彩空间问题,但似乎 RGBW 案例非常特定于物理照明/LED,不适用于数字显示器,RGBW 不包含在我看过的任何模块中在。

      这个模块的杀手锏功能是,您实例化的颜色对象可以根据您的需要(您创建它的不同颜色系统)在多个颜色系统中进行操作,并且它将所有翻译保留到其他空间最新的——而且它的速度非常快,我们还没有让它成为一个帧速率限制组件。

      因此,这样的事情将是通过完全明亮、完全饱和的彩虹的循环(注意 RGB 与 HSV 代码如何远不适合编程操作):

      from rgbw_colorspace_converter.colors.converters import RGB
      
      color = RGB(255,0,0)
      
      ctr = 0
      
      while ctr < 10:
           color.hsv_h += .1
           print(f"HSV:{color.hsv}  RGB:{color.rgb}  HSI:{color.hsi} HEX:{color.hex}")
           ctr += 1
      
      # "H" in hsv is actually expressed in 360 degrees, and it is cylindrical. We've normalized it to being between 0-1 (so H=0=H=1 - both are red)
      HSV:(0.0, 1.0, 1.0)  RGB:(255, 0, 0)  HSI:(0.0, 1.0, 0.33333) HEX:#ff0000
      HSV:(0.1, 1.0, 1.0)  RGB:(255, 153, 0)  HSI:(36.0, 1.0, 0.533328) HEX:#ff9900
      HSV:(0.2, 1.0, 1.0)  RGB:(203, 255, 0)  HSI:(72.23529411764707, 1.0, 0.5986868235294117) HEX:#cbff00
      HSV:(0.3, 1.0, 1.0)  RGB:(51, 255, 0)  HSI:(108.0, 1.0, 0.399996) HEX:#33ff00
      HSV:(0.4, 1.0, 1.0)  RGB:(0, 255, 102)  HSI:(144.0, 1.0, 0.46666199999999997) HEX:#00ff66
      HSV:(0.5, 1.0, 1.0)  RGB:(0, 255, 255)  HSI:(180.0, 1.0, 0.66666) HEX:#00ffff
      HSV:(0.6, 1.0, 1.0)  RGB:(0, 102, 255)  HSI:(216.0, 1.0, 0.46666199999999997) HEX:#0066ff
      HSV:(0.7, 1.0, 1.0)  RGB:(50, 0, 255)  HSI:(251.76470588235296, 1.0, 0.39868882352941176) HEX:#3200ff
      HSV:(0.8, 1.0, 1.0)  RGB:(204, 0, 255)  HSI:(288.0, 1.0, 0.599994) HEX:#cc00ff
      HSV:(0.9, 1.0, 1.0)  RGB:(255, 0, 152)  HSI:(324.2352941176471, 1.0, 0.5320208235294118) HEX:#ff0098
      HSV:(1.0, 1.0, 1.0)  RGB:(255, 0, 0)  HSI:(0.0, 1.0, 0.33333) HEX:#ff0000
      
      
      # AND- you can access the other color systems with
      # color.rgbw, color.hsi, color.hsl /// All of
      # which change as the RGB or HSV properties are changed 
      # (you can even change them in different spaces on the same object)
      

      【讨论】:

        【解决方案6】:

        我在这里可能有点偏离,但作为一名电子工程师,我看到 LED 输出光,然后编码 XD。无论如何,输出白色可以通过“点亮”所有 RGB 或白色通道来完成,但是保持相同的强度(以及从我的角度来看 LED 的良好状态)可以混合所有 4 个通道以输出与过驱动白色相同的白色渠道。 目前我怀疑这是否有任何问题,但以后可能会有所帮助。

        【讨论】:

        • 但是,如果我有一个非常高 CRI 的白色 LED,让 RGB 通道满载不会降低光的质量吗?
        【解决方案7】:

        我在研究我自己的 RGB 到 RGBW 转换时遇到了这个话题(这还不起作用:-),我似乎错过了这个转换中的一个重要因素,那就是白色 LED 的色温。 “RGB-white”的色温可能是 6500(至少对于我遇到的所有 RGB LED 来说似乎都是如此),并且您似乎将您的白色 LED 通道视为它也具有该色温,但对于 RGBW LED,4000 或 5000 更为常见...

        【讨论】:

        • 在下面查看我的答案 - 我提出了一个考虑色温的算法
        • @DanielMurphy 谢谢!我仍在苦苦挣扎的一件事(将近一年后)是 W-white 的强度不等于 RGB-white 的强度。将常数与白色 LED 的相对强度相乘的简单解决方案在低强度时会失效(可能是因为我没有考虑伽马校正)......
        猜你喜欢
        • 2014-02-02
        • 2012-04-11
        • 2016-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-29
        相关资源
        最近更新 更多