2010-11-08 18 views
5

leí el artículo Algorithm to Switch Between RGB and HSB Color ValuesAlgoritmo para conmutar entre RGB y HSB valores de color

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 

Entonces había alguien que ha publicado que había un error, pero no dio más detalles mucho

Pero creo lo necesitan de manejar cuando h es de más de 5, por ejemplo para el color 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 

¿Debo agregar ese fragmento de código? Y supongo que debe entrar en HSB a RGB (en mi C# conversión)

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

también, debe ser como la de arriba, o

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

Respuesta

9

Utilizando los métodos incorporados en objeto Color de .NET es un no-motor de arranque, ya que, como varias de las respuestas señalan, que no son compatibles con el revés (la conversión de un color HSB a RGB). Además, Color.GetBrightness vuelve realidad ligereza, en lugar de brillo/valor. Existe una gran confusión sobre las diferencias entre los espacios de color HSB/HSV y HSL debido a sus similitudes (Wikipedia). Veo muchos selectores de color que terminan usando el algoritmo y/o modelo incorrectos.

El código original Me parece que se echa de menos algunos escenarios posibles cuando se calcula el valor para el matiz, dado un color RGB. Es un poco difícil para mí seguir las adiciones que estás contemplando con el código, pero lo primero que salta a la vista (y que no pareces sugerir que lo corrijas) es que cuando la saturación = 0, estableces hue a -1. Cuando más tarde se multiplica por el tono 60, se termina con -60, a continuación, se agrega que al 360 (If h < 0 Then h = h + 360), produciendo un resultado de 300, lo cual no es correcto.

I usar el siguiente código (en VB.NET) para convertir entre RGB y HSB (que llamo HSV).Los resultados se han probado exhaustivamente y los resultados son prácticamente idénticos a los que ofrece el selector de color de Photoshop (aparte de la compensación que ofrece para los perfiles de color). La principal diferencia entre el código publicado y el mío (aparte de la parte importante que calcula el matiz) es que prefiero normalizar los valores RGB para que estén entre 0 y 1 para hacer los cálculos, en lugar de trabajar con los valores originales entre 0 y 255 Esto elimina algunas de las ineficiencias y conversiones múltiples en el código original que usted publicó, también.

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 

Usted no publicar el código que se utiliza para convertir de una HSB (que yo llamo HSV) de color a RGB, pero aquí es lo que yo uso, una vez más se trabaja con valores intermedios que se encuentran entre 0 y 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 

EDIT: Este código se ve muy similar a la proporcionada en C por Richard J. Ross III. Busqué tantos algoritmos diferentes como pude encontrar en línea, reescribí una gran cantidad de código tomando lo mejor de cada uno de ellos, e hice pruebas exhaustivas para verificar la precisión de los resultados. Me olvidé de señalar a quién me prestó el código, ya que esto era solo para una biblioteca privada. Tal vez la versión VB ayude a alguien que no quiere hacer una conversión de C. :-)

+0

Supongo que para los suyos, RGB variará de 0 a 255, entonces ¿qué pasa con HSB? ¿Es lo mismo que la versión @Richard J. Ross III C? ¿Que es eso? –

+0

Sí, RGB es de 0 a 255. El tono está entre 0 y 360, mientras que Saturación y Valor/Brillo están entre 0 y 100. Hue mode los grados de un círculo, mientras que Saturación y Valor/Brillo modelan el porcentaje de saturación de color o brillo, respectivamente. –

+1

Hmm, lo intenté, está bien, excepto que cuando hue = 360, se vuelve negro, ¿cómo puedo solucionarlo? –

2

Aquí está mi versión de cómo hacerlo (en C , lo siento, pero no debería ser difícil de convertir, basta con sustituir los int * 's y double *' s con out o ref enteros, y no usan la sintaxis de puntero)

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 a 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; 
} 

Si encuentro mi código en C#, voy a editar esta respuesta ....

+0

No soy un experto en colores, pero después de buscar implementaciones de HSB por un tiempo, descubrí que muchos están confundidos entre HSL y HSB, ¿el tuyo es HSB, supongo? Voy a probarlo en un momento ... –

+0

Sí, la mía es segura HSB, no HSL También tuve problemas con eso ... –

+0

Además, sus valores HSB son de qué rango a qué? Supongo que RGB es 0 - 255? –

1

¿Qué pasa con el uso de métodos de color GetBrightness, GetHue y GetSaturation?

+1

Creo que 'System.Drawing.Color.GetBrightness' es realmente luminosidad, no brillo –

1

Si está utilizando .NET, ¿por qué reinventar la rueda?

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

Creo' System.Drawing.Color.GetBrightness' es realmente luminosidad, no brillo –

1

La conversión de RGB a HSB debe ser bastante fácil usando la estructura 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 

No es compatible con el revés, sin embargo.

+1

Creo que 'System.Drawing.Color.GetBrightness' es realmente luminosidad, no brillo –

+0

@jiewmeng: Posiblemente, pero la documentación sí lo describe como *" Obtiene el matiz valor de brillo de saturación-saturación (HSB) para esta estructura de color. "*. – Guffa

0

Solución

Se puede calcular el componente de brillo, sencillamente, ya que es el máximo de R, G, y B (referencia: fórmula para RGB to HSV from the Rochester Institute of Technology). Puede escalarlo como quiera dividiendo por 255 y multiplicando por la escala. Esto es lo mismo que hace en su código existente:

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

Así, al final puede utilizar el incorporado en funciones de .NET y simplemente calcular su brillo. código completo sería (con exclusión de sus tipos):

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 

Un poco sobre HSB (igual que el HSV)

De Darel Rex Finley:

En el HSV (también llamado HSB) Sistema , el brillo de un color es su componente V. Ese componente se define simplemente como el valor máximo de cualquiera de los tres componentes RGB del color - los otros dos componentes RGB se ignoran cuando se determina V.

Según el la Microsoft Documentation para Color.GetBrightness:

Obtiene el brillo de saturación de tonalidad (HSB) valor de brillo para esta estructura de Color.

He encontrado algunas referencias que dicen que MSDN usa HSB cuando significa HSL como this one from MSDN blogs (ver los comentarios). Una prueba rápida demuestra que esto es cierto (en 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()); 

Esto resulta en un valor de 127, que es claramente HSL. Si era HSB, el valor debería ser el máximo de R G y B (es decir, 255).

+0

En realidad estoy buscando HSB no HSL. También usé esa clase de HSLColor antes de –

+0

. Estaba confundido por todos sus comentarios sobre GetBrightness en cuanto a lo que quería. Por favor mira mis ediciones – badbod99

Cuestiones relacionadas