画像処理画像処理
HSV空間; HSV Color Space

概要
HSV空間とは色空間の一つで色相(Hue)、彩度(Saturation)、明度(Value)の3変数で色を表現する。
色相は[0,6)の値を取り(文献によって異なる)、赤、黄、緑、シアン、青、マゼンダの色合いを表現する。
彩度は[0,1]の値を取り、鮮やかさを表現する。0ならば白から黒の無彩色、1ならば最も彩度が高い。
明度は[0,1]の値を取り、明るさを表現する。0ならば黒になる。
当サイトではアルファチャネルを加え透明度も考慮する。

HSV

RGBからHSVへの変換
RGB空間の色\(R, G, B\)を等価なHSV空間の色\(H, S, V\)に変換するには以下の式を用いる。

\( \quad \displaystyle C_{max} = Max(R, G, B) \)
\( \quad \displaystyle C_{min} = Min(R, G, B) \)
\( \quad \displaystyle H = \begin{cases} \displaystyle 0 &\quad C_{max} = C_{min} \\ \displaystyle \frac{G-B}{C_{max} - C_{min}} &\quad C_{max}=R ,\ G \geq B \\ \displaystyle \frac{B-R}{C_{max} - C_{min}} + 2 &\quad C_{max}=G \\ \displaystyle \frac{R-G}{C_{max} - C_{min}} + 4 &\quad C_{max}=B \\ \displaystyle \frac{G-B}{C_{max} - C_{min}} + 6 &\quad C_{max}=R ,\ G \lt B \\ \end{cases} \)
\( \quad \displaystyle S = \begin{cases} \displaystyle \frac{C_{max} - C_{min}}{C_{max}} &\quad C_{max} \gt 0 \\ \displaystyle 0 &\quad C_{max} = 0 \end{cases} \)
\( \quad \displaystyle V = C_{max} \)

HSVからRGBへの変換
HSV空間の色\(H, S, V\)を等価なRGB空間の色\(R, G, B\)に変換するには以下の式を用いる。

\(\quad \displaystyle d = \lfloor H \rfloor, \quad f = H - d \)
\( \quad \displaystyle R, G, B = \begin{cases} \displaystyle R = V, &\quad G = V (1-S(1-f)), &\quad B = V (1-S) &\quad d = 0 \\ \displaystyle R = V (1-S f), &\quad G = V, &\quad B = V (1-S) &\quad d = 1 \\ \displaystyle R = V (1-S), &\quad G = V, &\quad B = V (1-S(1-f)) &\quad d = 2 \\ \displaystyle R = V (1-S), &\quad G = V (1-S f), &\quad B = V &\quad d = 3 \\ \displaystyle R = V (1-S(1-f)), &\quad G = V (1-S), &\quad B = V &\quad d = 4 \\ \displaystyle R = V, &\quad G = V (1-S), &\quad B = V (1-S f) &\quad d = 5 \\ \end{cases} \)

ソースコード

namespace ColorField {

    /// <summary>HSV空間</summary>
    public unsafe struct HSV : IFormattable {
        private fixed double pixel[4];

        #region Constructor

        /// <summary>コンストラクタ</summary>
        public HSV(HSV cr) {
            H = cr.H;
            S = cr.S;
            V = cr.V;
            A = cr.A;
        }

        /// <summary>コンストラクタ</summary>
        public HSV(double h, double s, double v, double a) {
            H = h;
            S = s;
            V = v;
            A = a;
        }

        /// <summary>コンストラクタ</summary>
        public HSV(RGB cr) {
            double max_c = Math.Max(Math.Max(cr.R, cr.G), cr.B);
            double min_c = Math.Min(Math.Min(cr.R, cr.G), cr.B);
            
            H = max_c - min_c;
            S = (max_c > 0) ? (H / max_c) : 0;
            V = max_c;
            
            if(H > 0) {
                if(max_c == cr.R) {
                    H = (cr.G - cr.B) / H + ((cr.G >= cr.B) ? 0 : 6);
                }
                else if(max_c == cr.G) { 
                    H = (cr.B - cr.R) / H + 2;
                }
                else{ 
                    H = (cr.R - cr.G) / H + 4;
                }
            }

            A = cr.A;
        }

        #endregion

        
        #region Property

        /// <summary>色相</summary>
        public unsafe double H {
            get {
                fixed(double *pixel_ptr = pixel) {
                    return pixel_ptr[0];
                }
            }            
            set {
                fixed(double *pixel_ptr = pixel) {
                    pixel_ptr[0] = value;
                }
            }
        }

        /// <summary>彩度</summary>
        public unsafe double S {
            get {
                fixed(double *pixel_ptr = pixel) {
                    return pixel_ptr[1];
                }
            }            
            set {
                fixed(double *pixel_ptr = pixel) {
                    pixel_ptr[1] = value;
                }
            }
        }

        /// <summary>明度</summary>
        public unsafe double V {
            get {
                fixed(double *pixel_ptr = pixel) {
                    return pixel_ptr[2];
                }
            }            
            set {
                fixed(double *pixel_ptr = pixel) {
                    pixel_ptr[2] = value;
                }
            }
        }

        /// <summary>透明度</summary>
        public unsafe double A {
            get {
                fixed(double *pixel_ptr = pixel) {
                    return pixel_ptr[3];
                }
            }            
            set {
                fixed(double *pixel_ptr = pixel) {
                    pixel_ptr[3] = value;
                }
            }
        }

        /// <summary>有効な領域内の値であるか判定</summary>
        public bool IsValid {
            get {
                return (!(H < 0) && (H <= 6)) && (!(S < 0) && (S <= 1)) && (!(V < 0) && (V <= 1)) && (!(A < 0) && (A <= 1));
            }
        }

        #endregion


        #region IFormattable

        /// <summary>文字列化</summary>
        public override string ToString() {
            return $"{H},{S},{V},{A}";
        }

        /// <summary>文字列化</summary>
        public string ToString(string format) {
            return $"{H.ToString(format)},{S.ToString(format)},{V.ToString(format)},{A.ToString(format)}";
        }

        /// <summary>文字列化</summary>
        public string ToString(string format, IFormatProvider formatProvider) {
            return ToString(format);
        }

        #endregion

    }
}

単体テスト

namespace ColorFieldTests {
    [TestClass()]
    public class RGBTests {
        [TestMethod()]
        public void HSVRGBTest() {
            RGB rgb;
            
            rgb = new RGB(new HSV(0, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 0, 0, 1));
            rgb = new RGB(new HSV(1, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 1, 0, 1));
            rgb = new RGB(new HSV(2, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(0, 1, 0, 1));
            rgb = new RGB(new HSV(3, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(0, 1, 1, 1));
            rgb = new RGB(new HSV(4, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(0, 0, 1, 1));
            rgb = new RGB(new HSV(5, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
            
            rgb = new RGB(new HSV(6, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 0, 0, 1));
            rgb = new RGB(new HSV(7, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 1, 0, 1));

            rgb = new RGB(new HSV(-1, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
            rgb = new RGB(new HSV(-7, 1, 1, 1));
            Assert.AreEqual(rgb, new RGB(1, 0, 1, 1));
        }

        [TestMethod()]
        public void RGBHSVTest() {
            HSV hsv;
            
            hsv = new HSV(new RGB(1, 0, 0, 1));
            Assert.AreEqual(hsv, new HSV(0, 1, 1, 1));
            hsv = new HSV(new RGB(1, 0.5, 0, 1));
            Assert.AreEqual(hsv, new HSV(0.5, 1, 1, 1));
            hsv = new HSV(new RGB(1, 1, 0, 1));
            Assert.AreEqual(hsv, new HSV(1, 1, 1, 1));
            hsv = new HSV(new RGB(0.5, 1, 0, 1));
            Assert.AreEqual(hsv, new HSV(1.5, 1, 1, 1));
            hsv = new HSV(new RGB(0, 1, 0, 1));
            Assert.AreEqual(hsv, new HSV(2, 1, 1, 1));
            hsv = new HSV(new RGB(0, 1, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(2.5, 1, 1, 1));
            hsv = new HSV(new RGB(0, 1, 1, 1));
            Assert.AreEqual(hsv, new HSV(3, 1, 1, 1));
            hsv = new HSV(new RGB(0, 0.5, 1, 1));
            Assert.AreEqual(hsv, new HSV(3.5, 1, 1, 1));
            hsv = new HSV(new RGB(0, 0, 1, 1));
            Assert.AreEqual(hsv, new HSV(4, 1, 1, 1));
            hsv = new HSV(new RGB(0.5, 0, 1, 1));
            Assert.AreEqual(hsv, new HSV(4.5, 1, 1, 1));
            hsv = new HSV(new RGB(1, 0, 1, 1));
            Assert.AreEqual(hsv, new HSV(5, 1, 1, 1));
            hsv = new HSV(new RGB(1, 0, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(5.5, 1, 1, 1));

            hsv = new HSV(new RGB(0.5, 0.125, 0.125, 1));
            Assert.AreEqual(hsv, new HSV(0, 0.75, 0.5, 1));
            hsv = new HSV(new RGB(0.5, 0.5, 0.125, 1));
            Assert.AreEqual(hsv, new HSV(1, 0.75, 0.5, 1));
            hsv = new HSV(new RGB(0.125, 0.5, 0.125, 1));
            Assert.AreEqual(hsv, new HSV(2, 0.75, 0.5, 1));
            hsv = new HSV(new RGB(0.125, 0.5, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(3, 0.75, 0.5, 1));
            hsv = new HSV(new RGB(0.125, 0.125, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(4, 0.75, 0.5, 1));
            hsv = new HSV(new RGB(0.5, 0.125, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(5, 0.75, 0.5, 1));

            hsv = new HSV(new RGB(1, 1, 1, 1));
            Assert.AreEqual(hsv, new HSV(0, 0, 1, 1));
            hsv = new HSV(new RGB(0.5, 0.5, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(0, 0, 0.5, 1));
            hsv = new HSV(new RGB(0, 0, 0, 1));
            Assert.AreEqual(hsv, new HSV(0, 0, 0, 1));

            hsv = new HSV(new RGB(1.0, 0.75, 0.5, 1));
            Assert.AreEqual(hsv, new HSV(0.5, 0.5, 1, 1));
        }
    }
}

関連項目
RGB空間

ライブラリライブラリ
確率統計確率統計
線形代数線形代数
幾何学幾何学
最適化最適化
微分方程式微分方程式
画像処理画像処理
補間補間
機械学習機械学習
クラスタリングクラスタリング
パズルゲーム・パズル
未分類未分類