【问题标题】:Algorithm to Switch Between RGB and HSB Color Values在 RGB 和 HSB 颜色值之间切换的算法
【发布时间】:2010-11-08 12:53:19
【问题描述】:

我看了文章Algorithm to Switch Between RGB and HSB Color Values

Type RGBColor
     Red As Byte
     Green As Byte
     Blue As Byte
End Type

Type HSBColor
     Hue As Double
     Saturation As Double
     Brightness As Double
End Type

Function RGBToHSB(rgb As RGBColor) As HSBColor
     Dim minRGB, maxRGB, Delta As Double
     Dim h, s, b As Double
     h = 0
     minRGB = Min(Min(rgb.Red, rgb.Green), rgb.Blue)
     maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
     Delta = (maxRGB - minRGB)
     b = maxRGB
     If (maxRGB <> 0) Then
          s = 255 * Delta / maxRGB
     Else
          s = 0
     End If
     If (s <> 0) Then
          If rgb.Red = maxRGB Then
               h = (CDbl(rgb.Green) - CDbl(rgb.Blue)) / Delta
          Else
               If rgb.Green = maxRGB Then
                    h = 2 + (CDbl(rgb.Blue) - CDbl(rgb.Red)) / Delta
               Else
                    If rgb.Blue = maxRGB Then
                         h = 4 + (CDbl(rgb.Red) - CDbl(rgb.Green)) / Delta
                    End If
               End If
          End If
     Else
          h = -1
     End If
     h = h * 60
     If h < 0 Then h = h + 360
     RGBToHSB.Hue = h
     RGBToHSB.Saturation = s * 100 / 255
     RGBToHSB.Brightness = b * 100 / 255
End Function

Function HSBToRGB(hsb As HSBColor) As RGBColor
     Dim maxRGB, Delta As Double
     Dim h, s, b As Double
     h = hsb.Hue / 60
     s = hsb.Saturation * 255 / 100
     b = hsb.Brightness * 255 / 100
     maxRGB = b
     If s = 0 Then
          HSBToRGB.Red = 0
          HSBToRGB.Green = 0
          HSBToRGB.Blue = 0
     Else
          Delta = s * maxRGB / 255
          If h > 3 Then
               HSBToRGB.Blue = CByte(Round(maxRGB))
               If h > 4 Then
                    HSBToRGB.Green = CByte(Round(maxRGB - Delta))
                    HSBToRGB.Red = CByte(Round((h - 4) * Delta)) + HSBToRGB.Green
               Else
                    HSBToRGB.Red = CByte(Round(maxRGB - Delta))
                    HSBToRGB.Green = CByte(HSBToRGB.Red - Round((h - 4) * Delta))
               End If
          Else
               If h > 1 Then
                    HSBToRGB.Green = CByte(Round(maxRGB))
                    If h > 2 Then
                         HSBToRGB.Red = CByte(Round(maxRGB - Delta))
                         HSBToRGB.Blue = CByte(Round((h - 2) * Delta)) + HSBToRGB.Red
                    Else
                         HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
                         HSBToRGB.Red = CByte(HSBToRGB.Blue - Round((h - 2) * Delta))
                    End If
               Else
                    If h > -1 Then
                         HSBToRGB.Red = CByte(Round(maxRGB))
                         If h > 0 Then
                              HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
                              HSBToRGB.Green = CByte(Round(h * Delta)) + HSBToRGB.Blue
                         Else
                              HSBToRGB.Green = CByte(Round(maxRGB - Delta))
                              HSBToRGB.Blue = CByte(HSBToRGB.Green - Round(h * Delta))
                         End If
                    End If
               End If
          End If
     End If
End Function

然后有人发帖说有错误但没有详细说明

但我认为当 h 大于 5 时需要管理,例如颜色 R:130 G:65 B:111

If h > 5 Then
    HSBToRGB.Red = CByte(Round(maxRGB))
If h > 6 Then
    HSBToRGB.Blue= CByte(Round(maxRGB - Delta))
    HSBToRGB.Green= CByte(Round((h - 6) * Delta)) HSBToRGB.Blue
Else
    HSBToRGB.Green= CByte(Round(maxRGB - Delta))
    HSBToRGB.Blue = CByte(HSBToRGB.Green- Round((h - 6) * Delta))
End If

我需要添加那段代码吗?而且我认为它应该进入 HSB 到 RGB(在我的 C# 转换中)

...
if (s != 0) {
    delta = s * maxRGB / 255;
    if (h > 5)
        rgb.Red = Convert.ToByte(Math.Round(maxRGB));
    if (h > 6)
    {
        rgb.Green = Convert.ToByte(Math.Round(maxRGB - delta));
        rgb.Blue = Convert.ToByte(rgb.Green - Math.Round((h - 6) * delta));
    }
    if (h > 3)
    {
        ...

还有,应该像上面那样,还是

if (h > 6) { } 
else if (h > 3)  { }

【问题讨论】:

    标签: c# vb.net colors rgb hsb


    【解决方案1】:

    使用 .NET 的 Color 对象中内置的方法是行不通的,因为正如一些答案所指出的那样,它们不支持反向(将 HSB 颜色转换为 RGB)。此外,Color.GetBrightness 实际上返回 lightness,而不是亮度/值。由于 HSB/HSV 和 HSL 颜色空间之间的相似性,存在很多混淆 (Wikipedia)。我看到很多颜色选择器最终使用了错误的算法和/或模型。

    在给定 RGB 颜色的情况下,原始代码在计算色调值时似乎遗漏了一些可能的情况。我有点难以理解您正在考虑对代码添加的内容,但我首先想到的(而且您似乎没有建议更正)是当饱和度 = 0 时,您设置色调为-1。当您稍后将色调乘以 60 时,最终得到 -60,然后将其与 360 (If h &lt; 0 Then h = h + 360) 相加,结果为 300,这是不正确的。

    我使用以下代码(在 VB.NET 中)在 RGB 和 HSB(我称之为 HSV)之间进行转换。结果已经过非常广泛的测试,结果几乎与 Photoshop 的颜色选择器给出的结果相同(除了它对颜色配置文件所做的补偿)。发布的代码和我的代码之间的主要区别(除了计算色调的重要部分)是我更喜欢将 RGB 值标准化为 0 到 1 之间进行计算,而不是使用 0 到 255 之间的原始值。这也消除了您发布的原始代码中的一些低效率和多次转换。

    Public Function RGBtoHSV(ByVal R As Integer, ByVal G As Integer, ByVal B As Integer) As HSV
         ''# Normalize the RGB values by scaling them to be between 0 and 1
         Dim red As Decimal = R / 255D
         Dim green As Decimal = G / 255D
         Dim blue As Decimal = B / 255D
    
         Dim minValue As Decimal = Math.Min(red, Math.Min(green, blue))
         Dim maxValue As Decimal = Math.Max(red, Math.Max(green, blue))
         Dim delta As Decimal = maxValue - minValue
    
         Dim h As Decimal
         Dim s As Decimal
         Dim v As Decimal = maxValue
    
         ''# Calculate the hue (in degrees of a circle, between 0 and 360)
         Select Case maxValue
            Case red
               If green >= blue Then
                   If delta = 0 Then
                      h = 0
                   Else
                      h = 60 * (green - blue) / delta
                   End If
               ElseIf green < blue Then
                   h = 60 * (green - blue) / delta + 360
               End If
            Case green
               h = 60 * (blue - red) / delta + 120
            Case blue
               h = 60 * (red - green) / delta + 240
         End Select
    
         ''# Calculate the saturation (between 0 and 1)
         If maxValue = 0 Then
            s = 0
         Else
            s = 1D - (minValue / maxValue)
         End If
    
         ''# Scale the saturation and value to a percentage between 0 and 100
         s *= 100
         v *= 100
    
      ''# Return a color in the new color space
      Return New HSV(CInt(Math.Round(h, MidpointRounding.AwayFromZero)), _
                     CInt(Math.Round(s, MidpointRounding.AwayFromZero)), _
                     CInt(Math.Round(v, MidpointRounding.AwayFromZero)))
    End Function
    

    您没有发布用于将 HSB(我称之为 HSV)颜色转换为 RGB 的代码,但这是我使用的代码,再次使用介于 0 和 1 之间的中间值:

    Public Function HSVtoRGB(ByVal H As Integer, ByVal S As Integer, ByVal V As Integer) As RGB
         ''# Scale the Saturation and Value components to be between 0 and 1
         Dim hue As Decimal = H
         Dim sat As Decimal = S / 100D
         Dim val As Decimal = V / 100D
    
         Dim r As Decimal
         Dim g As Decimal
         Dim b As Decimal
    
         If sat = 0 Then
           ''# If the saturation is 0, then all colors are the same.
           ''# (This is some flavor of gray.)
            r = val
            g = val
            b = val
         Else
            ''# Calculate the appropriate sector of a 6-part color wheel
            Dim sectorPos As Decimal = hue / 60D
            Dim sectorNumber As Integer = CInt(Math.Floor(sectorPos))
    
            ''# Get the fractional part of the sector
            ''# (that is, how many degrees into the sector you are)
            Dim fractionalSector As Decimal = sectorPos - sectorNumber
    
            ''# Calculate values for the three axes of the color
            Dim p As Decimal = val * (1 - sat)
            Dim q As Decimal = val * (1 - (sat * fractionalSector))
            Dim t As Decimal = val * (1 - (sat * (1 - fractionalSector)))
    
            ''# Assign the fractional colors to red, green, and blue
            ''# components based on the sector the angle is in
            Select Case sectorNumber
               Case 0, 6
                  r = val
                  g = t
                  b = p
               Case 1
                  r = q
                  g = val
                  b = p
               Case 2
                  r = p
                  g = val
                  b = t
               Case 3
                  r = p
                  g = q
                  b = val
               Case 4
                  r = t
                  g = p
                  b = val
               Case 5
                  r = val
                  g = p
                  b = q
            End Select
         End If
    
         ''# Scale the red, green, and blue values to be between 0 and 255
         r *= 255
         g *= 255
         b *= 255
    
         ''# Return a color in the new color space
         Return New RGB(CInt(Math.Round(r, MidpointRounding.AwayFromZero)), _
                        CInt(Math.Round(g, MidpointRounding.AwayFromZero)), _
                        CInt(Math.Round(b, MidpointRounding.AwayFromZero)))
    End Function
    

    编辑:此代码看起来与 Richard J. Ross III 在 C 中提供的代码非常相似。我在网上找到了尽可能多的不同算法,重写了很多代码,借鉴了它们中最好的部分,并进行了广泛的测试以验证结果的准确性。我忽略了我从谁那里借来的代码,因为这只是一个私人图书馆。也许 VB 版本会帮助那些不想从 C 进行转换的人。:-)

    【讨论】:

    • 我猜对你来说,RGB 的范围是 0 - 255,那么 HSB 呢?和@Richard J. Ross III C 版本一样吗?这是什么?
    • 是的,RGB 是从 0 到 255。Hue 介于 0 到 360 之间,而 Saturation 和 Value/Brightness 介于 0 到 100 之间。Hue 模拟圆的度数,而 Saturation 和 Value/Brightness分别对颜色饱和度或亮度的百分比进行建模。
    • 嗯,我试过了,没问题,只是当hue = 360时,它变黑了,我该如何解决这个问题?
    • 优秀的捕获。是的,该代码将起作用,因为当色调 = 360 时,这与色调 = 0 相同。我也修改了上面发布的代码。
    • 我们都去了维基百科。哈哈。这段代码几乎是我自己实现它的方式。
    【解决方案2】:

    这是我关于如何做到这一点的版本(在 C 中,抱歉,但应该不难转换,只需将 int *double * 替换为 outref ints,并且不要使用指针语法)

    void colorlib_hsbtorgb(double hue, double saturation, double brightness, int *red, int *green, int *blue)
    {   
        if (saturation == 0)
        {
            *red = *green = *blue = brightness;
        }
        else
        {
            // the color wheel consists of 6 sectors. Figure out which sector you're in.
            double sectorPos = hue / 60.0;
            int sectorNumber = (int)(floor(sectorPos));
            // get the fractional part of the sector
            double fractionalSector = sectorPos - sectorNumber;
    
            // calculate values for the three axes of the color. 
            double p = brightness * (1.0 - saturation);
            double q = brightness * (1.0 - (saturation * fractionalSector));
            double t = brightness * (1.0 - (saturation * (1 - fractionalSector)));
    
            // assign the fractional colors to r, g, and b based on the sector the angle is in.
            switch (sectorNumber)
            {
                case 0:
                    *red = brightness;
                    *green = t;
                    *blue = p;
                    break;
                case 1:
                    *red = q;
                    *green = brightness;
                    *blue = p;
                    break;
                case 2:
                    *red = p;
                    *green = brightness;
                    *blue = t;
                    break;
                case 3:
                    *red = p;
                    *green = q;
                    *blue = brightness;
                    break;
                case 4:
                    *red = t;
                    *green = p;
                    *blue = brightness;
                    break;
                case 5:
                    *red = brightness;
                    *green = p;
                    *blue = q;
                    break;
            }
        }
    }
    

    RGB转hsb:

    void colorlib_rgbtohsb(int red, int green, int blue, double *hue, double *saturation, double *brightness)
    {
        double dRed = red / 255;
        double dGreen = green / 255;
        double dBlue = blue / 255;
    
        double max = fmax(dRed, fmax(dGreen, dBlue));
        double min = fmin(dRed, fmin(dGreen, dBlue));
    
        double h = 0;
        if (max == dRed && dGreen >= dBlue)
        {
            h = 60 * (dGreen - dBlue) / (max - min);
        }
        else if (max == dRed && dGreen < dBlue)
        {
            h = 60 * (dGreen - dBlue) / (max - min) + 360;
        }
        else if (max == dGreen)
        {
            h = 60 * (dBlue - dRed) / (max - min) + 120;
        }
        else if (max == dBlue)
        {
            h = 60 * (dRed - dGreen) / (max - min) + 240;
        }
    
        double s = (max == 0) ? 0.0 : (1.0 - (min / max));
    
        *hue = h;
        *saturation = s;
        *brightness = max;
    }
    

    如果我在 C# 中找到我的代码,我将编辑这个答案......

    【讨论】:

    • 我不是色彩专家,但是在寻找 HSB 实现一段时间后,我发现很多人对 HSL 和 HSB 感到困惑,我猜你的是 HSB 吗?一会儿我测试一下……
    • 另外,您的 HSB 值是从什么范围到什么?我假设 RGB 是 0 - 255?
    • HSB 是 0 到 1...double 值...可以通过乘以 255 切换到 0-255...
    【解决方案3】:

    如何使用 Color GetBrightness、GetHue 和 GetSaturation 方法?

    【讨论】:

    • 我认为System.Drawing.Color.GetBrightness实际上是亮度,而不是亮度
    【解决方案4】:

    如果您使用的是 .net,为什么要重新发明轮子?

    Dim c = Color.FromArgb(myRed, myGreen, myBlue)
    Dim h = c.GetHue()
    Dim s = c.GetSaturation()
    Dim b = c.GetBrightness()
    

    【讨论】:

    • 我认为System.Drawing.Color.GetBrightness实际上是光度,而不是亮度
    【解决方案5】:

    使用Color 结构从RGB 到HSB 的转换应该相当容易:

    Function RGBToHSB(rgb As RGBColor) As HSBColor
      Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
      RGBToHSB.Hue = c.GetHue()
      RGBToHSB.Saturation = c.GetSaturation()
      RGBToHSB.Brightness = c.GetBrightness()
    End Function
    

    不过,它不支持反向操作。

    【讨论】:

    • 我认为System.Drawing.Color.GetBrightness实际上是亮度,而不是亮度
    • @jiewmeng:可能,但文档确实将其描述为“获取此颜色结构的色调-饱和度-亮度 (HSB) 亮度值。”
    【解决方案6】:

    解决方案

    您可以非常简单地计算亮度分量,因为它是 R、G 和 B 的最大值(参考:RGB to HSV from the Rochester Institute of Technology 的公式)。您可以通过除以 255 并乘以比例来随意缩放它。这与您在现有代码中所做的相同:

    maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
    b = maxRGB    
    ...    
    RGBToHSB.Brightness = b * 100 / 255
    

    因此,最终您可以使用内置的 .Net 函数并计算您的亮度。完整代码将是(不包括您的类型):

    Function RGBToHSB(rgb As RGBColor) As HSBColor
      Dim maxRGB As Double
      maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
    
      Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
      RGBToHSB.Hue = c.GetHue()
      RGBToHSB.Saturation = c.GetSaturation() * 100
      RGBToHSB.Brightness = maxRGB * 100 / 255
    End Function
    

    关于 HSB(与 HSV 相同)的一些信息

    来自Darel Rex Finley

    在 HSV(也称为 HSB)系统中,颜色的亮度是其 V 分量。该分量被简单地定义为颜色的三个 RGB 分量中的任何一个的最大值——在确定 V 时忽略其他两个 RGB 分量。

    根据Microsoft DocumentationColor.GetBrightness

    获取此 Color 结构的色调饱和度 (HSB) 亮度值。

    我发现一些参考资料说 MSDN 使用 HSB,而这意味着像 this one from MSDN blogs 这样的 HSL(请参阅 cmets)。快速测试证明这是真的(在 C# 中):

    // Define a color which gives different HSL and HSB value
    Color c = Color.FromArgb(255, 0, 0);
    // Get the brightness, scale it from 0.0 - 1.0 up to 0 - 255
    int bright = (int)(c.GetBrightness() * 255.00);
    // Output it
    Console.WriteLine(bright.ToString());
    

    这会产生127 的值,这显然是HSL。如果是 HSB,则该值应该是 R G 和 B 的最大值(即255)。

    【讨论】:

    • 我实际上是在寻找 HSB 而不是 HSL。我之前也使用过那个 HSLColor 类
    • 您的所有 cmets 都对您想要的 GetBrightness 感到困惑。请查看我的编辑。
    猜你喜欢
    • 2011-05-05
    • 1970-01-01
    • 2012-07-27
    • 2020-11-03
    • 2011-06-24
    • 2018-07-18
    • 2014-02-02
    • 2012-01-03
    • 1970-01-01
    相关资源
    最近更新 更多