【问题标题】:C# RGB to HSL one way conversion algorithmC# RGB 到 HSL 单向转换算法
【发布时间】:2014-09-16 22:35:53
【问题描述】:

以下是我的RGB转HSL单向颜色转换方法,用C#写的。它紧密基于开源程序“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 值是 0.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));
                }
            }

【问题讨论】:

  • HSL 不应该在 [0,1] 范围内吗?真正的解决方案是 0,62280701754386, 0,301587301587301, 0,876470588235294。 S 和 L 是百分比,72 和 210 没有任何意义。
  • @Martin 那是双向转换,代码解决的范围比我的问题要大很多。我确实注意到了该页面,但无法将其应用于我的问题(也许那部分是我的错)。
  • @Pierre-Luc 我认为它是最常见的格式,但是当我使用 Meazure 检查像素的 HSL 颜色时得到的结果不受最大 100 的限制。 [0-1 ] 或 [0-100] 格式似乎是任意百分比表示,据我收集。
  • 但即使是您从 Meazure 提供的算法也能做到这一点。拿一个计算器,自己计算l 的值,(233 / 255 + 233 / 255) / 2 = 0.87..

标签: c# colors models rgb hsl


【解决方案1】:

你原来的算法有两个问题:

这里声明的变量从不使用:

    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;

第二个问题也在这个部分。输入是ints,你除以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');
}

【讨论】:

  • 感谢您的解决方案!对于为什么选择看似任意的 239 值,或者答案可能位于bitbucket.org/cthing/meazure/src/… 的哪个文件中,您是否有任何直觉?我发布的 C++ 代码来自 Colors.cpp,但我没有看到那里提到 239。
  • @user10478 如果您遵循MeaColors::RGBtoHSL 的用法,您可能会发现它在此过程中发生了哪些变化。它不一定在 Colors.cpp 中,某些类可以调用该方法并从那里更改结果。为此,您可以将源代码放在一个好的 IDE 中,并使用“查找用法”功能(我希望 C++ IDE 有这个功能)来跟踪 HSL 值的使用位置。
猜你喜欢
  • 1970-01-01
  • 2015-10-15
  • 2011-03-29
  • 2011-01-22
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 1970-01-01
相关资源
最近更新 更多