Алгоритм переключения между значениями цвета RGB и HSB
Вопрос
Я читаю статью Алгоритм переключения между значениями цвета 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)
{
...
Кроме того, если это будет похоже на выше, или
if (h > 6) { }
else if (h > 3) { }
Решение
Использование методов, встроенных в цветовой объект .NET, не является начальным, потому что, как указывают некоторые ответы, они не поддерживают обратное (преобразование цвета HSB в RGB). Кроме того, Color.GetBrightness
на самом деле возвращается Легкость, а не яркость/значение. Существует много путаницы из -за различий между цветными пространствами 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
РЕДАКТИРОВАТЬ: Этот код выглядит очень похожим на тот, который предоставлен в C Ричардом Дж. Россом III. Я выследил столько разных алгоритмов, сколько мог найти онлайн, переписал много кода, заимствовавшего лучшее у каждого из них, и прошел обширные тестирование, чтобы проверить точность результатов. Я пренебрегал, из кого я позаимствовал код, так как это было только для частной библиотеки. Может быть, версия VB поможет кому-то, кто не хочет делать обращение из C. :-)
Другие советы
Вот моя версия о том, как это сделать (в C, извините, но не сложно преобразовать, просто замените int *
'песок double *
с out
или ref
INT, и не используйте синтаксис указателя)
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#, я отредактирую этот ответ ....
А как насчет использования цветовой getbrightness, gethue и методов получения детектирования?
Если вы используете .net, зачем заново изобретать колесо?
Dim c = Color.FromArgb(myRed, myGreen, myBlue)
Dim h = c.GetHue()
Dim s = c.GetSaturation()
Dim b = c.GetBrightness()
Преобразование из 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
Это не поддерживает обратное, хотя.
Решение
Вы можете рассчитать компонент яркости довольно просто, так как это максимум R, G и B (ссылка: формула для RGB к HSV из технологического института Рочестера) Вы можете масштабировать его, как вам нравится, делясь на 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 цвета - два других компонента RGB игнорируются при определении V.
Согласно Документация Microsoft за Color.GetBrightness
:
Получает яркость насыщения хюэ (HSB) Значение яркости для этой цветовой структуры.
Я нашел некоторые ссылки, в которых говорится, что MSDN использует HSB, когда это означает HSL этот из блогов MSDN (См. Комментарии). Быстрый тест доказывает, что это правда (в 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, значение должно быть максимум RG и B (т.е. 255
).