DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

RGBをHSV(HSB)、HSL(HLS)、HSIに変換、復元する

ここでは、RGBをHSV(HSB)、HSL(HLS)、HSIに変換する方法と、その逆の変換をする方法を紹介します。なおこれら3種類の色空間については、「HSL and HSV - Wikipedia, the free encyclopedia」が参考になります。

RGBをHSV(HSB)に変換、復元する

.NET Frameworkでは色を表した型としてSystem.Drawing.Color構造体が用意されていますが、これはRGBカラーを表現しています。HSV(HSB)やHSL(HLS)、HSIを表現した型は用意されていません。HSLの各成分を計算するメソッドだけは用意されていますが、それ以外は自分で計算する必要があります。

まずはRGBをHSVに、HSVのをRGBに変換する方法を紹介します。「HSV色空間 - Wikipedia」で紹介されている計算方法を基にして作成したHSVカラーを表現したクラスの例を以下に示します。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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));
    }
}

RGBをHSL(HLS)に変換、復元する

Color構造体にはHSLの各成分を取得するメソッドとして、GetHueメソッド(色相)、GetSaturationメソッド(彩度)、GetBrightnessメソッド(明度)が用意されています。しかし、HSLからRGBに変換する方法は用意されていません。

以下に示すクラスでは、RGBからHSL、HSLからRGBの変換ができます。計算方法は、「HSL and HSV - Wikipedia, the free encyclopedia」を参考にしました。

なお以下の例では計算方法を紹介するために、RGBからHSLに変換する時もColor構造体に用意されているメソッドを使用せずに、自分で計算しています。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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に変換、復元する

以下に示すクラスで、RGBをHSIに、HSIをRGBに変換することができます。RGBからHSIへの変換は「HSL and HSV - Wikipedia, the free encyclopedia」を、HSIからRGBへの変換は「RGB to HSI, HSI to RGB Conversion Calculator」を参考にしました。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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));
    }
}

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。