将RGB转换为RGBW

时间:2016-10-28 19:44:21

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

我知道这个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值一直在底部 enter image description here

6 个答案:

答案 0 :(得分:7)

我终于想出了如何将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 };

enter image description here

任何优化创意都非常受欢迎:)

答案 1 :(得分:4)

我制定了一个算法,该算法考虑了“白色”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) {
  //Get the maximum between R, G, and B
  uint8_t r = rgb.r;
  uint8_t g = rgb.g;
  uint8_t b = rgb.b;

  // These values are what the 'white' value would need to2
  // 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 origina 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);
}

答案 2 :(得分:1)

我认为问题在于:

    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:

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不足以接近您未设置av的任何RGB值 - 它始终保持设置为零。这也将为您提供您所看到的一切都是黑色的结果

答案 3 :(得分:1)

我采取了另一种方法。我只检查最小值,然后将其设置为白色通道,然后从红色,绿色和蓝色通道中减去该值。这有点简化,但是效果很好并且易于实现。 128,128,0变为128,128,0,0 64,64,128变为0,0,64,64等 这还使得可以容易地实施颜色校正。说蓝色有点强(典型),当构建rgbw值时,将蓝色通道限制为较低的值,例如250而不是255。我不能说这在C或C ++中会很好用,但在ADA中会很好。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

相关问题