2010-11-08 48 views
5

我讀的文章Algorithm to Switch Between RGB and HSB Color Values算法RGB和HSB顏色之間切換值

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) 
    { 
     ... 

也,它應該是像上面,或

​​

回答

9

使用內置到.NET的Color對象中的方法是一個非啓動器,因爲正如幾個答案指出的那樣,它們不支持反轉(將HSB顏色轉換爲RGB)。此外,Color.GetBrightness實際上返回亮度,而不是亮度/值。由於HSB/HSV色彩空間的相似性(Wikipedia),在HSB/HSV和HSL色彩空間之間存在許多混淆。我看到很多最終使用錯誤的算法和/或模型的顏色選擇器。

原始代碼在我看來很喜歡它,它在計算給定RGB顏色的色調值時忽略了一些可能的場景。對我來說,遵循你正在考慮的代碼中的一些添加是有點困難的,但是第一件事就是跳到我的頭上(而且你似乎沒有建議更正)是當飽和度= 0時,你設置了色調爲-1。當你稍後將色相乘以60時,最終得到-60,然後將其加到360(If h < 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 

編輯:這個代碼看起來非常相似,由理查德·羅斯III使用C提供。我搜索了許多不同的算法,我找到了很多不同的算法,重新編寫了大量的代碼,並藉助它們中的每一個,並進行了大量測試以驗證結果的準確性。我沒有注意到我是從誰那裏借用代碼的,因爲這只是一個私人圖書館。也許VB版將幫助別人,誰不希望做從下轉換:-)

+0

我想對你來說,RGB的範圍是從0到255,那麼HSB呢? Isit與@Richard J. Ross III C版本相同嗎?那是什麼? – 2010-11-08 14:47:08

+0

是的,RGB是從0到255.色調在0到360之間,而飽和度和值/亮度在0和100之間。色調模擬圓的度數,而飽和度和值/亮度模擬色彩飽和度的百分比或亮度,分別。 – 2010-11-08 14:53:34

+1

嗯,我試過這個,除了當色相= 360時,它變成了黑色,我該如何解決這個問題? – 2010-11-09 06:47:12

2

下面是我對如何做到這一點的版本(在C ,很抱歉,但應該不難轉換,只是outref整數更換int *的和double *的,並且不使用指針語法)

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#中找到我的代碼,我將編輯這個答案....

+0

我不是一位色彩專家,但在尋找HSB實施一段時間後,我發現HSL和HSB之間有很多混淆,你認爲HSB是HSB?我會在一段時間內測試它... – 2010-11-08 13:36:28

+0

是的,我的確是HSB,而不是HSL我也有過這個問題... – 2010-11-08 13:41:07

+0

另外,你的HSB值是從什麼範圍到什麼?我認爲RGB是0 - 255? – 2010-11-08 14:14:18

1

怎麼樣使用Color GetBrightness,GetHue和GetSaturation方法呢?

+1

我認爲'System.Drawing.Color.GetBrightness'實際上是光度,而不是亮度 – 2010-11-08 13:34:55

1

如果您使用.net,爲什麼要重新發明輪子?

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

我認爲' System.Drawing.Color.GetBrightness'實際上是亮度,而不是亮度 – 2010-11-08 13:32:58

1

從RGB到HSB轉換應該用Color結構相當簡單:

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 

它不支持反轉,雖然。

+1

我認爲'System.Drawing.Color.GetBrightness'實際上是亮度,而不是亮度 – 2010-11-08 13:34:37

+0

@jiewmeng:可能,但文檔確實將其描述爲*「獲取色調 - 此顏色結構的飽和度 - 亮度(HSB)亮度值。「*。 – Guffa 2010-11-08 14:38:14

0

解決方案

可以計算亮度分量很簡單,因爲它的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.

當根據所述Microsoft DocumentationColor.GetBrightness其他兩個RGB分量被忽略:

獲取此Color結構的色調飽和度 - 亮度(HSB)亮度值。

我發現一些引用說MSDN使用HSB時,它意味着HSL像this one from MSDN blogs(見評論)。快速測試證明了這是真實的(在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)。

+0

我實際上是在尋找HSB而不是HSL。我還在 – 2010-11-08 14:48:18

+0

之前使用過HSLColor類,因爲您對GetBrightness的所有評論感到困惑。請參閱我的編輯。 – badbod99 2010-11-08 15:19:54