C#RGB到HSL的单向转换算法
本文关键字:转换 算法 HSL C#RGB | 更新日期: 2023-09-27 18:22:11
下面是我用C#编写的RGB到HSL单向颜色转换方法。它完全基于开源程序"Meazure"的代码(这是我在项目中使用的工具,因此我需要将HSL颜色的格式与Meazure中的格式相同)。
public String rgbToHsl(int r, int g, int b)
{
double h, s, l;
double rDecimal = r / 255;
double gDecimal = g / 255;
double bDecimal = b / 255;
double cMin = Math.Min(r, Math.Min(g, b));
double cMax = Math.Max(r, Math.Max(g, b));
double delta = cMax - cMin;
l = (cMax + cMin) / 2;
if (cMax == cMin)
{
s = 0;
h = 0; // It's really undefined
}
else
{
if (l < .5)
{
s = delta / (cMax + cMin);
}
else
{
s = delta / (2 - cMax - cMin);
}
if (r == cMax)
{
h = (g - b) / delta;
}
else if (g == cMax)
{
h = 2 + (b - r) / delta;
}
else
{
h = 4 + (r - g) / delta;
}
h /= 6;
if (h < 0)
{
h += 1;
}
}
return h.ToString().PadLeft(3, '0') + s.ToString().PadLeft(3, '0') + l.ToString().PadLeft(3, '0');
}
以下是我作为参考使用的开源、C++Meazure代码。
void MeaColors::RGBtoHSL(COLORREF rgb, HSL& hsl)
{
double h, s, l;
double r = GetRValue(rgb) / 255.0;
double g = GetGValue(rgb) / 255.0;
double b = GetBValue(rgb) / 255.0;
double cmax = Max(r, Max(g, b));
double cmin = Min(r, Min(g, b));
l = (cmax + cmin) / 2.0;
if (MEA_DBL_EQL(cmax, cmin)) {
s = 0.0;
h = 0.0; // it's really undefined
} else {
if (l < 0.5) {
s = (cmax - cmin) / (cmax + cmin);
} else {
s = (cmax - cmin) / (2.0 - cmax - cmin);
}
double delta = cmax - cmin;
if (MEA_DBL_EQL(r, cmax)) {
h = (g - b) / delta;
} else if (MEA_DBL_EQL(g, cmax)) {
h = 2.0 + (b - r) / delta;
} else {
h = 4.0 + (r - g) / delta;
}
h /= 6.0;
if (h < 0.0) {
h += 1.0;
}
}
hsl.hue = h;
hsl.lightness = l;
hsl.saturation = s;
}
问题是我的方法没有输出预期的值。它可以编译和运行,不会崩溃。然而,对于输入RGB值214、219、233,我的方法产生HSL值6、228、70,而通过使用Meazure以HSL格式测量相同像素而获得的期望值是149、72、210。我在这个网站上注意到了一些类似的问题,但没有一个是功能性的解决方案,至少是这里想要的格式。
这是我的代码,它调用了我的方法。它恰好通过在输入像素周围构建一个5x5框来从单个输入中转换25个RGB值(刚才提到这一点是为了避免混淆)。
Bitmap screenShot = takeScreenShot();
const int squareSideSize = 5;
Color[] firstPixelSquare = new Color[(int)Math.Pow(squareSideSize, 2)];
hslColor[] hslFirstPixelSquare = new hslColor[(int)Math.Pow(squareSideSize, 2)];
for (int hOffset = -2, i = 0; hOffset <= 2; hOffset++, i += squareSideSize)
{
for (int vOffset = -2, j = i; vOffset <= 2; vOffset++, j++)
{
firstPixelSquare[j] = screenShot.GetPixel((int)numericUpDownX1.Value + hOffset, (int)numericUpDownY1.Value + vOffset);
hslFirstPixelSquare[j].h = Convert.ToDouble(rgbToHsl(firstPixelSquare[j].R, firstPixelSquare[j].G, firstPixelSquare[j].B).Substring(0, 3));
hslFirstPixelSquare[j].s = Convert.ToDouble(rgbToHsl(firstPixelSquare[j].R, firstPixelSquare[j].G, firstPixelSquare[j].B).Substring(3, 3));
hslFirstPixelSquare[j].l = Convert.ToDouble(rgbToHsl(firstPixelSquare[j].R, firstPixelSquare[j].G, firstPixelSquare[j].B).Substring(6, 3));
}
}
原始算法有两个问题:
此处声明的变量从未使用过:
double rDecimal = r / 255;
double gDecimal = g / 255;
double bDecimal = b / 255;
将r
切换为rDecimal
,或者简单地重命名输入以继续使用r, g, b
变量:
public String RgbToHsl(int rInput, int gInput, int bInput)
{
double h, s, l;
double r = rInput / 255;
double g = gInput / 255;
double b = bInput / 255;
第二个问题也在这个环节。输入是int
s,除以255(这也是int
)。这会产生一个整数除法,每次都会产生0。你需要除以255.0才能强制进行双重除法:
public String RgbToHsl(int rInput, int gInput, int bInput)
{
double h, s, l;
double r = rInput / 255.0;
double g = gInput / 255.0;
double b = bInput / 255.0;
完成后,您需要将[0,1]区间的结果转换为您从工具中获得的结果。我所能达到的最接近的结果是将结果乘以239,然后四舍五入。也许可以从更多的输入/输出示例中找到真正的模式/确切的值。。。
h = Math.Round(h * 239);
s = Math.Round(s * 239);
l = Math.Round(l * 239);
return h.ToString().PadLeft(3, '0') + s.ToString().PadLeft(3, '0') + l.ToString().PadLeft(3, '0');
}