Algorithme pour basculer entre les valeurs de couleur RVB et HSB
Question
j'ai lu l'article Algorithme pour basculer entre les valeurs de couleur RVB et 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
Ensuite, quelqu'un a posté qu'il y avait une erreur mais n'a pas donné beaucoup de détails.
Mais je pense qu'il faut gérer quand h est supérieur à 5, par exemple pour la couleur 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
Dois-je ajouter ce morceau de code ?Et je suppose qu'il devrait passer de HSB à RVB (dans ma conversion 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)
{
...
aussi, est-ce que ça devrait être comme ci-dessus, ou
if (h > 6) { }
else if (h > 3) { }
La solution
L'utilisation des méthodes intégrées à l'objet Color de .NET est un échec car, comme le soulignent plusieurs réponses, elles ne prennent pas en charge l'inverse (conversion d'une couleur HSB en RVB).En plus, Color.GetBrightness
revient effectivement légèreté, plutôt que la luminosité/valeur.Il existe beaucoup de confusion sur les différences entre les espaces colorimétriques HSB/HSV et HSL en raison de leurs similitudes (Wikipédia).Je vois beaucoup de sélecteurs de couleurs qui finissent par utiliser le mauvais algorithme et/ou modèle.
Le code original me semble manquer quelques scénarios possibles lorsqu'il calcule la valeur de la teinte, étant donné une couleur RVB.C'est un peu difficile pour moi de suivre les ajouts que vous envisagez dans le code, mais la première chose qui me saute aux yeux (et que vous ne semblez pas suggérer de corriger) c'est que lorsque la saturation = 0, on met teinte à -1.Lorsque vous multipliez plus tard la teinte par 60, vous obtenez -60, puis vous l'ajoutez à 360 (If h < 0 Then h = h + 360
), produisant un résultat de 300, ce qui n'est pas correct.
J'utilise le code suivant (en VB.NET) pour convertir entre RVB et HSB (que j'appelle HSV).Les résultats ont été testés de manière très approfondie et sont pratiquement identiques à ceux donnés par le sélecteur de couleurs de Photoshop (mis à part la compensation qu'il effectue pour les profils de couleurs).La principale différence entre le code publié et le mien (mis à part la partie importante qui calcule la teinte) est que je préfère normaliser les valeurs RVB entre 0 et 1 pour effectuer les calculs, plutôt que de travailler avec les valeurs d'origine comprises entre 0 et 255. .Cela élimine également certaines des inefficacités et des conversions multiples dans le code d'origine que vous avez publié.
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
Vous n'avez pas publié le code que vous utilisez pour convertir une couleur HSB (que j'appelle HSV) en RVB, mais voici ce que j'utilise, en travaillant encore une fois avec des valeurs intermédiaires comprises entre 0 et 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
MODIFIER: Ce code ressemble beaucoup à celui fourni en C par Richard J.Ross III.J'ai recherché autant d'algorithmes différents que possible en ligne, j'ai réécrit de nombreux codes en empruntant le meilleur de chacun d'eux et j'ai effectué des tests approfondis pour vérifier l'exactitude des résultats.J'ai négligé de noter à qui j'avais emprunté le code, car il s'agissait uniquement d'une bibliothèque privée.Peut-être que la version VB aidera quelqu'un qui ne souhaite pas effectuer de conversion à partir de C.:-)
Autres conseils
Voici ma version sur la façon de le faire (en C, désolé, mais ne devrait pas être difficile à convertir, il suffit de remplacer pour de la int *
et double *
avec INTS out
ou ref
, et ne pas utiliser la syntaxe du pointeur)
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;
}
Si je trouve mon code en C #, je modifier cette réponse ....
Qu'est-ce que sur l'utilisation de couleurs GetBrightness, les méthodes et GetHue GetSaturation?
Si vous utilisez .net, pourquoi réinventer la roue?
Dim c = Color.FromArgb(myRed, myGreen, myBlue)
Dim h = c.GetHue()
Dim s = c.GetSaturation()
Dim b = c.GetBrightness()
La conversion de RGB à HSB devrait être assez facile en utilisant la structure 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
Il ne supporte pas l'inverse, cependant.
Solution
Vous pouvez calculer la composante de la luminosité tout simplement comme il est le maximum de R, G et B (référence: formule RGB à HSV de Rochester Institute of Technology ). Vous pouvez escalader cependant vous en divisant par 255 et en multipliant par l'échelle. Ceci est la même chose que fait dans votre code existant:
maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
b = maxRGB
...
RGBToHSB.Brightness = b * 100 / 255
Alors, à la fin vous pouvez utiliser intégré dans les fonctions .Net et juste calculer la luminosité. code complet serait (à l'exception de vos types):
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 peu de HSB (identique à HSV)
De Darel Rex Finley :
Dans le système HSV (également appelé HSB), la luminosité d'une couleur est la composante V. est défini que le composant simplement comme la valeur maximale de l'une des trois composantes RVB de la couleur. - les deux autres composantes RGB sont ignorés lors de la détermination V
Selon le Microsoft Documentation Color.GetBrightness
:
Obtient la teinte-saturation-luminosité ( HSB ) la valeur de luminosité pour cette structure Color.
J'ai trouvé quelques références disant que le MSDN utilise HSB quand cela signifie HSL comme celui-ci à partir des blogs MSDN (voir les commentaires). Un test rapide prouve que cela est vrai (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());
Il en résulte une valeur de 127
, qui est clairement HSL. Si elle était HSB la valeur doit être au maximum de R G et B (à savoir 255
).