ここでは、RGBをHSV(HSB)、HSL(HLS)、HSIに変換する方法と、その逆の変換をする方法を紹介します。なおこれら3種類の色空間については、「HSL and HSV - Wikipedia, the free encyclopedia」が参考になります。
.NET Frameworkでは色を表した型としてSystem.Drawing.Color構造体が用意されていますが、これはRGBカラーを表現しています。HSV(HSB)やHSL(HLS)、HSIを表現した型は用意されていません。HSLの各成分を計算するメソッドだけは用意されていますが、それ以外は自分で計算する必要があります。
まずはRGBをHSVに、HSVのをRGBに変換する方法を紹介します。「HSV色空間 - Wikipedia」で紹介されている計算方法を基にして作成したHSVカラーを表現したクラスの例を以下に示します。
'Imports System.Drawing ''' <summary> ''' HSV (HSB) カラーを表す ''' </summary> Public Class HsvColor Private _h As Single ''' <summary> ''' 色相 (Hue) ''' </summary> Public ReadOnly Property H() As Single Get Return Me._h End Get End Property Private _s As Single ''' <summary> ''' 彩度 (Saturation) ''' </summary> Public ReadOnly Property S() As Single Get Return Me._s End Get End Property Private _v As Single ''' <summary> ''' 明度 (Value, Brightness) ''' </summary> Public ReadOnly Property V() As Single Get Return Me._v End Get End Property Private Sub New(ByVal hue As Single, _ ByVal saturation As Single, _ ByVal brightness As Single) If hue < 0.0F OrElse 360.0F <= hue Then Throw New ArgumentException( _ "hueは0以上360未満の値です。", "hue") End If If saturation < 0.0F OrElse 1.0F < saturation Then Throw New ArgumentException( _ "saturationは0以上1以下の値です。", "saturation") End If If brightness < 0.0F OrElse 1.0F < brightness Then Throw New ArgumentException( _ "brightnessは0以上1以下の値です。", "brightness") End If Me._h = hue Me._s = saturation Me._v = brightness End Sub ''' <summary> ''' 指定したColorからHsvColorを作成する ''' </summary> ''' <param name="rgb">Color</param> ''' <returns>HsvColor</returns> Public Shared Function FromRgb(ByVal rgb As Color) As HsvColor Dim r As Single = CSng(rgb.R) / 255.0F Dim g As Single = CSng(rgb.G) / 255.0F Dim b As Single = CSng(rgb.B) / 255.0F Dim max As Single = Math.Max(r, Math.Max(g, b)) Dim min As Single = Math.Min(r, Math.Min(g, b)) Dim brightness As Single = max Dim hue As Single, saturation As Single If max = min Then 'undefined hue = 0.0F saturation = 0.0F Else Dim c As Single = max - min If max = r Then hue = (g - b) / c ElseIf max = g Then hue = (b - r) / c + 2.0F Else hue = (r - g) / c + 4.0F End If hue *= 60.0F If hue < 0.0F Then hue += 360.0F End If saturation = c / max End If Return New HsvColor(hue, saturation, brightness) End Function ''' <summary> ''' 指定したHsvColorからColorを作成する ''' </summary> ''' <param name="hsv">HsvColor</param> ''' <returns>Color</returns> Public Shared Function ToRgb(ByVal hsv As HsvColor) As Color Dim v As Single = hsv.V Dim s As Single = hsv.S Dim r As Single, g As Single, b As Single If s = 0 Then r = v g = v b = v Else Dim h As Single = hsv.H / 60.0F Dim i As Integer = CInt(Math.Floor(h)) Dim f As Single = h - i Dim p As Single = v * (1.0F - s) Dim q As Single If i Mod 2 = 0 Then 't q = v * (1.0F - (1.0F - f) * s) Else q = v * (1.0F - f * s) End If Select Case i Case 0 r = v g = q b = p Exit Select Case 1 r = q g = v b = p Exit Select Case 2 r = p g = v b = q Exit Select Case 3 r = p g = q b = v Exit Select Case 4 r = q g = p b = v Exit Select Case 5 r = v g = p b = q Exit Select Case Else Throw New ArgumentException( _ "色相の値が不正です。", "hsv") End Select End If Return Color.FromArgb(CInt(Math.Round(r * 255.0F)), _ CInt(Math.Round(g * 255.0F)), _ CInt(Math.Round(b * 255.0F))) End Function End Class
//using System.Drawing; /// <summary> /// HSV (HSB) カラーを表す /// </summary> public class HsvColor { private float _h; /// <summary> /// 色相 (Hue) /// </summary> public float H { get { return this._h; } } private float _s; /// <summary> /// 彩度 (Saturation) /// </summary> public float S { get { return this._s; } } private float _v; /// <summary> /// 明度 (Value, Brightness) /// </summary> public float V { get { return this._v; } } private HsvColor(float hue, float saturation, float brightness) { if (hue < 0f || 360f <= hue) { throw new ArgumentException( "hueは0以上360未満の値です。", "hue"); } if (saturation < 0f || 1f < saturation) { throw new ArgumentException( "saturationは0以上1以下の値です。", "saturation"); } if (brightness < 0f || 1f < brightness) { throw new ArgumentException( "brightnessは0以上1以下の値です。", "brightness"); } this._h = hue; this._s = saturation; this._v = brightness; } /// <summary> /// 指定したColorからHsvColorを作成する /// </summary> /// <param name="rgb">Color</param> /// <returns>HsvColor</returns> public static HsvColor FromRgb(Color rgb) { float r = (float)rgb.R / 255f; float g = (float)rgb.G / 255f; float b = (float)rgb.B / 255f; float max = Math.Max(r, Math.Max(g, b)); float min = Math.Min(r, Math.Min(g, b)); float brightness = max; float hue, saturation; if (max == min) { //undefined hue = 0f; saturation = 0f; } else { float c = max - min; if (max == r) { hue = (g - b) / c; } else if (max == g) { hue = (b - r) / c + 2f; } else { hue = (r - g) / c + 4f; } hue *= 60f; if (hue < 0f) { hue += 360f; } saturation = c / max; } return new HsvColor(hue, saturation, brightness); } /// <summary> /// 指定したHsvColorからColorを作成する /// </summary> /// <param name="hsv">HsvColor</param> /// <returns>Color</returns> public static Color ToRgb(HsvColor hsv) { float v = hsv.V; float s = hsv.S; float r, g, b; if (s == 0) { r = v; g = v; b = v; } else { float h = hsv.H / 60f; int i = (int)Math.Floor(h); float f = h - i; float p = v * (1f - s); float q; if (i % 2 == 0) { //t q = v * (1f - (1f - f) * s); } else { q = v * (1f - f * s); } switch (i) { case 0: r = v; g = q; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = q; break; case 3: r = p; g = q; b = v; break; case 4: r = q; g = p; b = v; break; case 5: r = v; g = p; b = q; break; default: throw new ArgumentException( "色相の値が不正です。", "hsv"); } } return Color.FromArgb( (int)Math.Round(r * 255f), (int)Math.Round(g * 255f), (int)Math.Round(b * 255f)); } }
Color構造体にはHSLの各成分を取得するメソッドとして、GetHueメソッド(色相)、GetSaturationメソッド(彩度)、GetBrightnessメソッド(明度)が用意されています。しかし、HSLからRGBに変換する方法は用意されていません。
以下に示すクラスでは、RGBからHSL、HSLからRGBの変換ができます。計算方法は、「HSL and HSV - Wikipedia, the free encyclopedia」を参考にしました。
なお以下の例では計算方法を紹介するために、RGBからHSLに変換する時もColor構造体に用意されているメソッドを使用せずに、自分で計算しています。
'Imports System.Drawing ''' <summary> ''' HSL (HLS) カラーを表す ''' </summary> Public Class HslColor Private _h As Single ''' <summary> ''' 色相 (Hue) ''' </summary> Public ReadOnly Property H() As Single Get Return Me._h End Get End Property Private _s As Single ''' <summary> ''' 彩度 (Saturation) ''' </summary> Public ReadOnly Property S() As Single Get Return Me._s End Get End Property Private _l As Single ''' <summary> ''' 輝度 (Lightness) ''' </summary> Public ReadOnly Property L() As Single Get Return Me._l End Get End Property Private Sub New(ByVal hue As Single, _ ByVal saturation As Single, _ ByVal lightness As Single) If hue < 0.0F OrElse 360.0F <= hue Then Throw New ArgumentException( _ "hueは0以上360未満の値です。", "hue") End If If saturation < 0.0F OrElse 1.0F < saturation Then Throw New ArgumentException( _ "saturationは0以上1以下の値です。", "saturation") End If If lightness < 0.0F OrElse 1.0F < lightness Then Throw New ArgumentException( _ "lightnessは0以上1以下の値です。", "lightness") End If Me._h = hue Me._s = saturation Me._l = lightness End Sub ''' <summary> ''' 指定したColorからHslColorを作成する ''' </summary> ''' <param name="rgb">Color</param> ''' <returns>HslColor</returns> Public Shared Function FromRgb(ByVal rgb As Color) As HslColor Dim r As Single = CSng(rgb.R) / 255.0F Dim g As Single = CSng(rgb.G) / 255.0F Dim b As Single = CSng(rgb.B) / 255.0F Dim max As Single = Math.Max(r, Math.Max(g, b)) Dim min As Single = Math.Min(r, Math.Min(g, b)) Dim lightness As Single = (max + min) / 2.0F Dim hue As Single, saturation As Single If max = min Then 'undefined hue = 0.0F saturation = 0.0F Else Dim c As Single = max - min If max = r Then hue = (g - b) / c ElseIf max = g Then hue = (b - r) / c + 2.0F Else hue = (r - g) / c + 4.0F End If hue *= 60.0F If hue < 0.0F Then hue += 360.0F End If 'saturation = c / (1.0F - Math.Abs(2.0F * lightness - 1.0F)) If lightness < 0.5F Then saturation = c / (max + min) Else saturation = c / (2.0F - max - min) End If End If Return New HslColor(hue, saturation, lightness) End Function ''' <summary> ''' 指定したHslColorからColorを作成する ''' </summary> ''' <param name="hsl">HslColor</param> ''' <returns>Color</returns> Public Shared Function ToRgb(ByVal hsl As HslColor) As Color Dim s As Single = hsl.S Dim l As Single = hsl.L Dim r1 As Single, g1 As Single, b1 As Single If s = 0 Then r1 = l g1 = l b1 = l Else Dim h As Single = hsl.H / 60.0F Dim i As Integer = CInt(Math.Floor(h)) Dim f As Single = h - i 'Dim c As Single = (1.0F - Math.Abs(2.0F * l - 1.0F)) * s Dim c As Single If l < 0.5F Then c = 2.0F * s * l Else c = 2.0F * s * (1.0F - l) End If Dim m As Single = l - c / 2.0F Dim p As Single = c + m 'Dim x As Single = c * (1.0F - Math.Abs(h Mod 2.0F - 1.0F)) Dim q As Single ' q = x + m If i Mod 2 = 0 Then q = l + c * (f - 0.5F) Else q = l - c * (f - 0.5F) End If Select Case i Case 0 r1 = p g1 = q b1 = m Exit Select Case 1 r1 = q g1 = p b1 = m Exit Select Case 2 r1 = m g1 = p b1 = q Exit Select Case 3 r1 = m g1 = q b1 = p Exit Select Case 4 r1 = q g1 = m b1 = p Exit Select Case 5 r1 = p g1 = m b1 = q Exit Select Case Else Throw New ArgumentException( _ "色相の値が不正です。", "hsl") End Select End If Return Color.FromArgb(CInt(Math.Round(r1 * 255.0F)), _ CInt(Math.Round(g1 * 255.0F)), _ CInt(Math.Round(b1 * 255.0F))) End Function End Class
//using System.Drawing; /// <summary> /// HSL (HLS) カラーを表す /// </summary> public class HslColor { private float _h; /// <summary> /// 色相 (Hue) /// </summary> public float H { get { return this._h; } } private float _s; /// <summary> /// 彩度 (Saturation) /// </summary> public float S { get { return this._s; } } private float _l; /// <summary> /// 輝度 (Lightness) /// </summary> public float L { get { return this._l; } } private HslColor(float hue, float saturation, float lightness) { if (hue < 0f || 360f <= hue) { throw new ArgumentException( "hueは0以上360未満の値です。", "hue"); } if (saturation < 0f || 1f < saturation) { throw new ArgumentException( "saturationは0以上1以下の値です。", "saturation"); } if (lightness < 0f || 1f < lightness) { throw new ArgumentException( "lightnessは0以上1以下の値です。", "lightness"); } this._h = hue; this._s = saturation; this._l = lightness; } /// <summary> /// 指定したColorからHslColorを作成する /// </summary> /// <param name="rgb">Color</param> /// <returns>HslColor</returns> public static HslColor FromRgb(Color rgb) { float r = (float)rgb.R / 255f; float g = (float)rgb.G / 255f; float b = (float)rgb.B / 255f; float max = Math.Max(r, Math.Max(g, b)); float min = Math.Min(r, Math.Min(g, b)); float lightness = (max + min) / 2f; float hue, saturation; if (max == min) { //undefined hue = 0f; saturation = 0f; } else { float c = max - min; if (max == r) { hue = (g - b) / c; } else if (max == g) { hue = (b - r) / c + 2f; } else { hue = (r - g) / c + 4f; } hue *= 60f; if (hue < 0f) { hue += 360f; } //saturation = c / (1f - Math.Abs(2f * lightness - 1f)); if (lightness < 0.5f) { saturation = c / (max + min); } else { saturation = c / (2f - max - min); } } return new HslColor(hue, saturation, lightness); } /// <summary> /// 指定したHslColorからColorを作成する /// </summary> /// <param name="hsl">HslColor</param> /// <returns>Color</returns> public static Color ToRgb(HslColor hsl) { float s = hsl.S; float l = hsl.L; float r1, g1, b1; if (s == 0) { r1 = l; g1 = l; b1 = l; } else { float h = hsl.H / 60f; int i = (int)Math.Floor(h); float f = h - i; //float c = (1f - Math.Abs(2f * l - 1f)) * s; float c; if (l < 0.5f) { c = 2f * s * l; } else { c = 2f * s * (1f - l); } float m = l - c / 2f; float p = c + m; //float x = c * (1f - Math.Abs(h % 2f - 1f)); float q; // q = x + m if (i % 2 == 0) { q = l + c * (f - 0.5f); } else { q = l - c * (f - 0.5f); } switch (i) { case 0: r1 = p; g1 = q; b1 = m; break; case 1: r1 = q; g1 = p; b1 = m; break; case 2: r1 = m; g1 = p; b1 = q; break; case 3: r1 = m; g1 = q; b1 = p; break; case 4: r1 = q; g1 = m; b1 = p; break; case 5: r1 = p; g1 = m; b1 = q; break; default: throw new ArgumentException( "色相の値が不正です。", "hsl"); } } return Color.FromArgb( (int)Math.Round(r1 * 255f), (int)Math.Round(g1 * 255f), (int)Math.Round(b1 * 255f)); } }
以下に示すクラスで、RGBをHSIに、HSIをRGBに変換することができます。RGBからHSIへの変換は「HSL and HSV - Wikipedia, the free encyclopedia」を、HSIからRGBへの変換は「RGB to HSI, HSI to RGB Conversion Calculator」を参考にしました。
'Imports System.Drawing ''' <summary> ''' HSI カラーを表す ''' </summary> Public Class HsiColor Private _h As Single ''' <summary> ''' 色相 (Hue) ''' </summary> Public ReadOnly Property H() As Single Get Return Me._h End Get End Property Private _s As Single ''' <summary> ''' 彩度 (Saturation, Chroma) ''' </summary> Public ReadOnly Property S() As Single Get Return Me._s End Get End Property Private _i As Single ''' <summary> ''' 輝度 (Intensity) ''' </summary> Public ReadOnly Property I() As Single Get Return Me._i End Get End Property Private Sub New(ByVal hue As Single, _ ByVal saturation As Single, _ ByVal intensity As Single) If hue < 0.0F OrElse 360.0F <= hue Then Throw New ArgumentException( _ "hueは0以上360未満の値です。", "hue") End If If saturation < 0.0F OrElse 1.0F < saturation Then Throw New ArgumentException( _ "saturationは0以上1以下の値です。", "saturation") End If If intensity < 0.0F OrElse 1.0F < intensity Then Throw New ArgumentException( _ "intensityは0以上1以下の値です。", "intensity") End If Me._h = hue Me._s = saturation Me._i = intensity End Sub ''' <summary> ''' 指定したColorからHsiColorを作成する ''' </summary> ''' <param name="rgb">Color</param> ''' <returns>HsiColor</returns> Public Shared Function FromRgb(ByVal rgb As Color) As HsiColor Dim r As Single = CSng(rgb.R) / 255.0F Dim g As Single = CSng(rgb.G) / 255.0F Dim b As Single = CSng(rgb.B) / 255.0F Dim max As Single = Math.Max(r, Math.Max(g, b)) Dim min As Single = Math.Min(r, Math.Min(g, b)) Dim intensity As Single = (r + g + b) / 3.0F Dim hue As Single, saturation As Single If max = min Then 'undefined hue = 0.0F saturation = 0.0F Else Dim c As Single = max - min If max = r Then hue = (g - b) / c ElseIf max = g Then hue = (b - r) / c + 2.0F Else hue = (r - g) / c + 4.0F End If hue *= 60.0F If hue < 0.0F Then hue += 360.0F End If saturation = 1.0F - min / intensity End If Return New HsiColor(hue, saturation, intensity) End Function ''' <summary> ''' 指定したHsiColorからColorを作成する ''' </summary> ''' <param name="hsi">HsiColor</param> ''' <returns>Color</returns> Public Shared Function ToRgb(ByVal hsi As HsiColor) As Color Dim r As Double, g As Double, b As Double Const p As Double = Math.PI / 180.0 Dim i As Single = hsi.I Dim s As Single = hsi.S Dim h As Single = hsi.H Dim si As Single = hsi.I * hsi.S If h < 120.0F Then Dim cos1 As Double = Math.Cos(h * p) Dim cos2 As Double = Math.Cos((60.0F - h) * p) r = i + si * cos1 / cos2 'g = i + si * (1 - cos1 / cos2) b = i - si g = 3.0F * i - r - b ElseIf h < 240.0F Then Dim cos1 As Double = Math.Cos((h - 120.0F) * p) Dim cos2 As Double = Math.Cos((180.0F - h) * p) r = i - si g = i + si * cos1 / cos2 'b = i + si * (1 - cos1 / cos2) b = 3.0F * i - r - g ElseIf h < 360.0F Then Dim cos1 As Double = Math.Cos((h - 240.0F) * p) Dim cos2 As Double = Math.Cos((300.0F - h) * p) 'r = i + si * (1 - cos1 / cos2) g = i - si b = i + si * cos1 / cos2 r = 3.0F * i - g - b Else Throw New ArgumentException( _ "色相の値が不正です。", "hsi") End If Return Color.FromArgb(CInt(Math.Round(r * 255.0F)), _ CInt(Math.Round(g * 255.0F)), _ CInt(Math.Round(b * 255.0F))) End Function End Class
//using System.Drawing; /// <summary> /// HSI カラーを表す /// </summary> public class HsiColor { private float _h; /// <summary> /// 色相 (Hue) /// </summary> public float H { get { return this._h; } } private float _s; /// <summary> /// 彩度 (Saturation, Chroma) /// </summary> public float S { get { return this._s; } } private float _i; /// <summary> /// 輝度 (Intensity) /// </summary> public float I { get { return this._i; } } private HsiColor(float hue, float saturation, float intensity) { if (hue < 0f || 360f <= hue) { throw new ArgumentException( "hueは0以上360未満の値です。", "hue"); } if (saturation < 0f || 1f < saturation) { throw new ArgumentException( "saturationは0以上1以下の値です。", "saturation"); } if (intensity < 0f || 1f < intensity) { throw new ArgumentException( "intensityは0以上1以下の値です。", "intensity"); } this._h = hue; this._s = saturation; this._i = intensity; } /// <summary> /// 指定したColorからHsiColorを作成する /// </summary> /// <param name="rgb">Color</param> /// <returns>HsiColor</returns> public static HsiColor FromRgb(Color rgb) { float r = (float)rgb.R / 255f; float g = (float)rgb.G / 255f; float b = (float)rgb.B / 255f; float max = Math.Max(r, Math.Max(g, b)); float min = Math.Min(r, Math.Min(g, b)); float intensity = (r + g + b) / 3f; float hue, saturation; if (max == min) { //undefined hue = 0f; saturation = 0f; } else { float c = max - min; if (max == r) { hue = (g - b) / c; } else if (max == g) { hue = (b - r) / c + 2f; } else { hue = (r - g) / c + 4f; } hue *= 60f; if (hue < 0f) { hue += 360f; } saturation = 1f - min / intensity; } return new HsiColor(hue, saturation, intensity); } /// <summary> /// 指定したHsiColorからColorを作成する /// </summary> /// <param name="hsi">HsiColor</param> /// <returns>Color</returns> public static Color ToRgb(HsiColor hsi) { double r, g, b; const double p = Math.PI / 180.0; float i = hsi.I; float s = hsi.S; float h = hsi.H; float si = hsi.I * hsi.S; if (h < 120f) { double cos1 = Math.Cos(h * p); double cos2 = Math.Cos((60f - h) * p); r = i + si * cos1 / cos2; //g = i + si * (1 - cos1 / cos2); b = i - si; g = 3f * i - r - b; } else if (h < 240f) { double cos1 = Math.Cos((h - 120f) * p); double cos2 = Math.Cos((180f - h) * p); r = i - si; g = i + si * cos1 / cos2; //b = i + si * (1 - cos1 / cos2); b = 3f * i - r - g; } else if (h < 360f) { double cos1 = Math.Cos((h - 240f) * p); double cos2 = Math.Cos((300f - h) * p); //r = i + si * (1 - cos1 / cos2); g = i - si; b = i + si * cos1 / cos2; r = 3f * i - g - b; } else { throw new ArgumentException( "色相の値が不正です。", "hsi"); } return Color.FromArgb( (int)Math.Round(r * 255f), (int)Math.Round(g * 255f), (int)Math.Round(b * 255f)); } }