将RGB颜色转换为HSB颜色

8

我正在尝试将HSB颜色转换为RGB。我所采用的方法是:

System.Windows.Media.Color winColor = value;
System.Drawing.Color drawColor = System.Drawing.Color.FromArgb(winColor.R, winColor.G, winColor.B);
Hue = (byte)(drawColor.GetHue()*255);
Saturation = (byte)(drawColor.GetSaturation()*255);
Luminosity = (byte)(drawColor.GetBrightness()*255);

我发现当我有FF0000时,它会转换为H = 0,S = 255,L = 127,这相当于RGBFF0E0E。我认为亮度应该是120吗?还是我完全没有理解HSB的概念?当我在Photoshop的调色板中查看颜色选择器时,色相值是0-360度,饱和度和亮度值是0-100%。我的HSB值范围为0 - 255,我做错了吗?


HSB 通常被表示为 3 个字节并不罕见 - 这样做精度较低,但仍然是颜色系统的有效表示。 - Bradley Smith
有争议。当然,他将值转换为字节而不是保留双精度浮点数是让他感到困惑的原因。这会在转换回来时产生微妙的不同值。 - Hans Passant
4个回答

14

也许你已经查看了这个维基百科文章,但为了让它更清晰明了。

HSL和HSB(又称HSV)之间存在差异。

因此,您不能从颜色类中获取(B)亮度并像使用(L)亮度一样使用它。

要从提供的Color类值GetHue()GetSaturation()GetBrightness()返回到普通颜色,您应该尝试使用这个扩展方法。

/// <summary>
/// Creates a Color from alpha, hue, saturation and brightness.
/// </summary>
/// <param name="alpha">The alpha channel value.</param>
/// <param name="hue">The hue value.</param>
/// <param name="saturation">The saturation value.</param>
/// <param name="brightness">The brightness value.</param>
/// <returns>A Color with the given values.</returns>
public static Color FromAhsb(int alpha, float hue, float saturation, float brightness)
{
    if (0 > alpha
        || 255 < alpha)
    {
        throw new ArgumentOutOfRangeException(
            "alpha",
            alpha,
            "Value must be within a range of 0 - 255.");
    }

    if (0f > hue
        || 360f < hue)
    {
        throw new ArgumentOutOfRangeException(
            "hue",
            hue,
            "Value must be within a range of 0 - 360.");
    }

    if (0f > saturation
        || 1f < saturation)
    {
        throw new ArgumentOutOfRangeException(
            "saturation",
            saturation,
            "Value must be within a range of 0 - 1.");
    }

    if (0f > brightness
        || 1f < brightness)
    {
        throw new ArgumentOutOfRangeException(
            "brightness",
            brightness,
            "Value must be within a range of 0 - 1.");
    }

    if (0 == saturation)
    {
        return Color.FromArgb(
                            alpha,
                            Convert.ToInt32(brightness * 255),
                            Convert.ToInt32(brightness * 255),
                            Convert.ToInt32(brightness * 255));
    }

    float fMax, fMid, fMin;
    int iSextant, iMax, iMid, iMin;

    if (0.5 < brightness)
    {
        fMax = brightness - (brightness * saturation) + saturation;
        fMin = brightness + (brightness * saturation) - saturation;
    }
    else
    {
        fMax = brightness + (brightness * saturation);
        fMin = brightness - (brightness * saturation);
    }

    iSextant = (int)Math.Floor(hue / 60f);
    if (300f <= hue)
    {
        hue -= 360f;
    }

    hue /= 60f;
    hue -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
    if (0 == iSextant % 2)
    {
        fMid = (hue * (fMax - fMin)) + fMin;
    }
    else
    {
        fMid = fMin - (hue * (fMax - fMin));
    }

    iMax = Convert.ToInt32(fMax * 255);
    iMid = Convert.ToInt32(fMid * 255);
    iMin = Convert.ToInt32(fMin * 255);

    switch (iSextant)
    {
        case 1:
            return Color.FromArgb(alpha, iMid, iMax, iMin);
        case 2:
            return Color.FromArgb(alpha, iMin, iMax, iMid);
        case 3:
            return Color.FromArgb(alpha, iMin, iMid, iMax);
        case 4:
            return Color.FromArgb(alpha, iMid, iMin, iMax);
        case 5:
            return Color.FromArgb(alpha, iMax, iMin, iMid);
        default:
            return Color.FromArgb(alpha, iMax, iMid, iMin);
    }
}

更新

为了澄清事情,我的代码和 Color 类中提到的三种方法都使用 HSB(也称为 HSV)颜色模型,但 Photoshop 使用的是 HSL 颜色模型。

在您的评论中,您写道参数 Hue = 0Saturation = 1Brightness = 1 在上面的代码中给出了红色,在 Photoshop 中则是白色。当您仔细观察这些模式的差异时,这完全合理:

HSL 圆柱体

HSL cylinder
(来源:wikimedia.org

  • 在两种模型中,色调都以红色为起点和终点(零度和 360 度)。
    • 仅凭色调,您就得到了红色。
  • 饱和度定义了颜色的不透明程度或白色比例的多少。
    • 因此,设置为 1 表示您想要一个完美闪亮的红色。
  • 光照现在定义了颜色中黑色和白色部分的多少。将其设置为零会得到黑色,设置为一表示白色,0.5 表示权重完美。
    • 因此,将其设置为 1 表示您希望它尽可能明亮,这将导致白色。

HSB 圆柱体

HSB cylinder
(来源:wikimedia.org

  • 在两种模型中,色调都以红色为起点和终点(零度和 360 度)。
    • 仅凭色调,您就得到了红色。
  • 饱和度定义了颜色的不透明程度或白色比例的多少。
    • 因此,设置为 1 表示您想要一个完美闪亮的红色。
  • 亮度(或值)现在定义了颜色中黑色部分的多少(而不是白色部分)。
    • 因此,将其设置为 1 表示您希望它完全有色,从而得到完美闪亮的红色。

正如您所看到的,Photoshop 和 .Net Framework(包括我的扩展函数)使用不同的着色模型。因此,您应该检查是否可以找到其他着色模型的实现、变换或其他内容,以获得所需的结果。


当我将色相设置为0,饱和度设置为1,亮度设置为1时,我得到的是白色。但在Photoshop中,我得到的是红色。 - Jiew Meng
1
似乎有一些关于名称的混淆。Microsoft的Color.GetBrightness()范围在0到1之间。如果你将它乘以240,你就会得到经典的亮度值,其中0表示黑色(没有光),240表示白色。Photoshop确实使用HSB模型(其中B=100%并不意味着白色):只需打开Photoshop的颜色选择器进行确认即可... - hemme
值得注意的是,Color.GetBrightness() 方法返回颜色的 HSL 亮度,而不是颜色的 HSB 亮度。https://learn.microsoft.com/en-us/dotnet/api/system.drawing.color.getbrightness?view=netframework-4.8 - Tom Carroll

3

这是从Java源代码修改的,它可以正常工作。另一个答案仍然是HSL,而这个确实是HSB转RGB(实际上是HSB/HSV转为System.Windows.Media.Color作为我的返回类型)。

    public static Color HSBtoRGB(float hue, float saturation, float brightness)
    {
        int r = 0, g = 0, b = 0;
        if (saturation == 0)
        {
            r = g = b = (int)(brightness * 255.0f + 0.5f);
        }
        else
        {
            float h = (hue - (float)Math.Floor(hue)) * 6.0f;
            float f = h - (float)Math.Floor(h);
            float p = brightness * (1.0f - saturation);
            float q = brightness * (1.0f - saturation * f);
            float t = brightness * (1.0f - (saturation * (1.0f - f)));
            switch ((int)h)
            {
                case 0:
                    r = (int)(brightness * 255.0f + 0.5f);
                    g = (int)(t * 255.0f + 0.5f);
                    b = (int)(p * 255.0f + 0.5f);
                    break;
                case 1:
                    r = (int)(q * 255.0f + 0.5f);
                    g = (int)(brightness * 255.0f + 0.5f);
                    b = (int)(p * 255.0f + 0.5f);
                    break;
                case 2:
                    r = (int)(p * 255.0f + 0.5f);
                    g = (int)(brightness * 255.0f + 0.5f);
                    b = (int)(t * 255.0f + 0.5f);
                    break;
                case 3:
                    r = (int)(p * 255.0f + 0.5f);
                    g = (int)(q * 255.0f + 0.5f);
                    b = (int)(brightness * 255.0f + 0.5f);
                    break;
                case 4:
                    r = (int)(t * 255.0f + 0.5f);
                    g = (int)(p * 255.0f + 0.5f);
                    b = (int)(brightness * 255.0f + 0.5f);
                    break;
                case 5:
                    r = (int)(brightness * 255.0f + 0.5f);
                    g = (int)(p * 255.0f + 0.5f);
                    b = (int)(q * 255.0f + 0.5f);
                    break;
            }
        }
        return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b));
    }

这是从Java源代码修改的吗?http://www.docjar.com/html/api/java/awt/Color.java.html第839行 - FelisPhasma

0

您可以使用ColorHelper库来实现此功能:

using ColorHelper;

RGB rgb = new RGB(20, 20, 20);
HSV hsv = ColorConverter.RgbToHsv(rgb);

链接:


0

由于其他答案似乎对我不起作用(而且我没有耐心去看发生了什么),我分享了我的HSV->RGB转换代码。它基于维基百科上的“替代HSV转换”部分,非常紧凑。

public static Color FromAhsv(byte alpha, float hue, float saturation, float value)
{
    if (hue < 0f || hue > 360f)
        throw new ArgumentOutOfRangeException(nameof(hue), hue, "Hue must be in the range [0,360]");
    if (saturation < 0f || saturation > 1f)
        throw new ArgumentOutOfRangeException(nameof(saturation), saturation, "Saturation must be in the range [0,1]");
    if (value < 0f || value > 1f)
        throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be in the range [0,1]");

    int Component(int n)
    {
        var k = (n + hue / 60f) % 6;
        var c = value - value * saturation * Math.Max(Math.Min(Math.Min(k, 4 - k), 1), 0);
        var b = (int)Math.Round(c * 255);
        return b < 0 ? 0 : b > 255 ? 255 : b;
    }

    return Color.FromArgb(alpha, Component(5), Component(3), Component(1));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接