我阅读了文章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) { }
答案 0 :(得分:9)
使用.NET的Color对象中内置的方法是不可能的,因为正如几个答案所指出的那样,它们不支持反向(将HSB颜色转换为RGB)。此外,Color.GetBrightness
实际上返回亮度,而不是亮度/值。由于HSB / HSV和HSL颜色空间的相似性(Wikipedia),因此存在很多混淆。我看到很多颜色选择器最终使用了错误的算法和/或模型。
在给出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
编辑:此代码与Richard J. Ross III在C中提供的代码非常相似。我在网上找到了尽可能多的不同算法,重写了大量代码,从每个代码中借用了最好的代码,并进行了大量测试以验证结果的准确性。我忽略了我从谁借来代码,因为这只是一个私人图书馆。也许VB版本会帮助那些不想从C转换的人: - )
答案 1 :(得分:2)
以下是关于如何操作的我的版本(在C中,抱歉,但不应该难以转换,只需将int *
和double *
替换为out
或ref
整数,并且不使用指针语法)
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 to 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#中找到我的代码,我会编辑这个答案....
答案 2 :(得分:1)
使用Color GetBrightness,GetHue和GetSaturation方法怎么样?
答案 3 :(得分:1)
如果您正在使用.net,为什么要重新发明轮子?
Dim c = Color.FromArgb(myRed, myGreen, myBlue)
Dim h = c.GetHue()
Dim s = c.GetSaturation()
Dim b = c.GetBrightness()
答案 4 :(得分:1)
使用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
但是,它不支持反向。
答案 5 :(得分: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相同)
在HSV(也称为HSB)系统中,颜色的亮度是其V分量。该组件简单地定义为颜色的三个RGB分量中的任何一个的最大值 - 在确定V时忽略其他两个RGB分量。
根据Color.GetBrightness
的{{3}}:
获取此Color结构的色调饱和度 - 亮度( HSB )亮度值。
我发现一些引用说MSDN使用HSB时它意味着像Microsoft Documentation这样的HSL(参见注释)。快速测试证明这是真的(在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
)。