将 RGB 转换为 RGBW
Converting RGB to RGBW
我知道这个 has already been asked,但是那里给出的答案不起作用。我花了一个多小时寻找公式或算法,但一无所获。因此,我开始编写自己的算法,以尽可能最有效的方式将 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值一直在底部
我认为问题出在这里:
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
- 它始终设置为零。这也会给你你所看到的一切都是黑色的结果
我终于知道怎么把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 };
欢迎任何优化想法:)
我在这里可能有点偏离,但作为一名电子工程师,我看到 LED 输出光然后编码 XD。无论如何输出白色可以通过 "lighting" 所有 rgb 或白色通道来完成,但保持相同的强度(从我的角度来看 LED 的良好状态)所有 4 个通道可以混合输出与过度驱动的白色通道。
我怀疑这在这一点上有什么问题,但以后可能会有帮助。
我采取了不同的方法。我只检查最低值并将其设为白色通道,然后从红色、绿色和蓝色通道中减去该值。这有点简化,但效果很好并且易于实现。
128,128,0 变成 128,128,0,0
64,64,128 变为 0,0,64,64 等。
这也允许轻松实现色彩校正。假设蓝色有点强(典型),在构建 rgbw 值时将蓝色通道限制为较低的值,例如 250 而不是 255。我不能说这在 C 或 C++ 中运行良好,但在 ADA 中运行良好。
我在研究我自己的 RGB 到 RGBW 转换时遇到了这个话题(这还不起作用:-),我似乎错过了这个转换中的一个重要因素,那就是白色 LED 的色温. “RGB-white”的色温可能是 6500(至少对于我遇到的所有 RGB LED 来说似乎都是如此),并且您似乎将白色 LED 通道视为也具有该色温,但是 4000 或 5000 对于 RGBW LED 更为常见...
我制定了一个算法,该算法考虑了您的“白色”LED 的色温(因为这可能因灯带而异 - 我的是暖白色,即 4500k)。要点是 here, code is below, and blog post with more context is 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);
}
这个 repo 可以为您完成这项工作:https://github.com/iamh2o/rgbw_colorspace_converter/
我和一个朋友一起写了这个模块,这样 'color' 对象可以通过几个颜色系统实例化,并且对象可以吐出翻译到它支持的所有其他颜色系统 - 经过很多研究(一个关键部分是 https://www.neltnerlabs.com/saikoled/how-to-convert-from-hsi-to-rgb-white ),我们最终确定了 [HSI/HSL/HSV/RGB/HEX] -> RGBW 转换。
有大量的软件包解决了一般的色彩空间问题,但 RGBW 的情况似乎非常特定于物理 lighting/leds,不适用于数字显示器,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)
我知道这个 has already been asked,但是那里给出的答案不起作用。我花了一个多小时寻找公式或算法,但一无所获。因此,我开始编写自己的算法,以尽可能最有效的方式将 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值一直在底部
我认为问题出在这里:
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
- 它始终设置为零。这也会给你你所看到的一切都是黑色的结果
我终于知道怎么把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 };
欢迎任何优化想法:)
我在这里可能有点偏离,但作为一名电子工程师,我看到 LED 输出光然后编码 XD。无论如何输出白色可以通过 "lighting" 所有 rgb 或白色通道来完成,但保持相同的强度(从我的角度来看 LED 的良好状态)所有 4 个通道可以混合输出与过度驱动的白色通道。 我怀疑这在这一点上有什么问题,但以后可能会有帮助。
我采取了不同的方法。我只检查最低值并将其设为白色通道,然后从红色、绿色和蓝色通道中减去该值。这有点简化,但效果很好并且易于实现。 128,128,0 变成 128,128,0,0 64,64,128 变为 0,0,64,64 等。 这也允许轻松实现色彩校正。假设蓝色有点强(典型),在构建 rgbw 值时将蓝色通道限制为较低的值,例如 250 而不是 255。我不能说这在 C 或 C++ 中运行良好,但在 ADA 中运行良好。
我在研究我自己的 RGB 到 RGBW 转换时遇到了这个话题(这还不起作用:-),我似乎错过了这个转换中的一个重要因素,那就是白色 LED 的色温. “RGB-white”的色温可能是 6500(至少对于我遇到的所有 RGB LED 来说似乎都是如此),并且您似乎将白色 LED 通道视为也具有该色温,但是 4000 或 5000 对于 RGBW LED 更为常见...
我制定了一个算法,该算法考虑了您的“白色”LED 的色温(因为这可能因灯带而异 - 我的是暖白色,即 4500k)。要点是 here, code is below, and blog post with more context is 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);
}
这个 repo 可以为您完成这项工作:https://github.com/iamh2o/rgbw_colorspace_converter/
我和一个朋友一起写了这个模块,这样 'color' 对象可以通过几个颜色系统实例化,并且对象可以吐出翻译到它支持的所有其他颜色系统 - 经过很多研究(一个关键部分是 https://www.neltnerlabs.com/saikoled/how-to-convert-from-hsi-to-rgb-white ),我们最终确定了 [HSI/HSL/HSV/RGB/HEX] -> RGBW 转换。
有大量的软件包解决了一般的色彩空间问题,但 RGBW 的情况似乎非常特定于物理 lighting/leds,不适用于数字显示器,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)