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

概要
RGB空間とは色空間の一つで赤・緑・青の3輝度で色を表現する。
赤・緑・青の輝度100%の色はデバイスによって異なるので、光源色を与えXYZ色空間や均等色空間であるLa*b*空間に変換して同じ色であるか、またどれたけ2色が離れているか比較する。
RGB空間はベクトル空間とは異なり、それぞれの輝度値の和・差は意味を成さないが、平均または加重平均はRGB空間上の色の平均として解釈できる。
RGB空間上の元である色の二項演算として加法混色、減法混色、アルファブレンディングなどが定義できる。
当サイトではアルファチャネルを加え透明度も考慮する。

加法混色
背景色の輝度と透明度\(C_{back}, A_{back}\)とその色に重ねる色\(C_{over}, A_{over}\)の2色を加法で混ぜた色\(C_{super}, A_{super}\)は以下で定義される。
色を重ねると白に近づく

\(\quad \displaystyle C_{super} = \frac{C_{back} A_{back} + C_{over} A_{over}}{A} \)
\(\quad \displaystyle A_{super} = 1 - (1 - A_{back}) \cdot (1 - A_{over}) \)

back_pepper
over_sailboat
addtiveblend1

back_circle
over_cross
addtiveblend2


減法混色
背景色の輝度と透明度\(C_{back}, A_{back}\)とその色に重ねる色\(C_{over}, A_{over}\)の2色を減法で混ぜた色\(C_{super}, A_{super}\)は以下で定義される。
色を重ねると黒に近づく

\(\quad \displaystyle C_{super} = \frac{C_{back} A_{back} - C_{over} A_{over}}{A} \)
\(\quad \displaystyle A_{super} = 1 - (1 - A_{back}) \cdot (1 - A_{over}) \)

back_pepper
over_sailboat
subtractiveblend1

back_circle
over_cross
subtractiveblend2

アルファブレンデング
背景色の輝度と透明度\(C_{back}, A_{back}\)とその色に重ねる色\(C_{over}, A_{over}\)の2色をアルファブレンデングで混ぜた色\(C_{super}, A_{super}\)は以下で定義される。
有色のフィルターを重ねた色になる。画像編集ソフトで標準的に使われている混色法である。

\(\quad \displaystyle C_{super} = \frac{C_{back} (A_{super} - A_{over}) + C_{over} A_{over}}{A} \)
\(\quad \displaystyle A_{super} = 1 - (1 - A_{back}) \cdot (1 - A_{over}) \)

back_pepper
over_sailboat
alphablend1

back_circle
over_cross
alphablend2

ソースコード

namespace ColorField {

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

        #region Constructor

        /// <summary>コンストラクタ</summary>
        public RGB(RGB cr) {
            R = cr.R;
            G = cr.G;
            B = cr.B;
            A = cr.A;
        }

        /// <summary>コンストラクタ</summary>
        public RGB(double r, double g, double b, double a) {
            R = r;
            G = g;
            B = b;
            A = a;
        }

        /// <summary>コンストラクタ</summary>
        public RGB(HSV cr) {
            A = cr.A;
            R = G = B = cr.V;
            if(cr.S > 0) {
                double d = Math.Floor(cr.H);
                double f = cr.H - d;
                int i = (int)d;

                if(i >= 6) {
                    i %= 6;
                }
                else if(i < 0) {
                    i = 6 - (-i % 6);
                }

                switch(i) {
                    case 0:
                        G *= 1 - cr.S * (1 - f);
                        B *= 1 - cr.S;
                        break;
                    case 1:
                        R *= 1 - cr.S * f;
                        B *= 1 - cr.S;
                        break;
                    case 2:
                        R *= 1 - cr.S;
                        B *= 1 - cr.S * (1 - f);
                        break;
                    case 3:
                        R *= 1 - cr.S;
                        G *= 1 - cr.S * f;
                        break;
                    case 4:
                        R *= 1 - cr.S * (1 - f);
                        G *= 1 - cr.S;
                        break;
                    case 5:
                        G *= 1 - cr.S;
                        B *= 1 - cr.S * f;
                        break;
                }
            }
        }

        #endregion


        #region Property

        /// <summary>赤輝度</summary>
        public unsafe double R {
            get {
                fixed (double* pixel_ptr = pixel)
                {
                    return pixel_ptr[(uint)ColorIndex.R];
                }
            }
            set {
                fixed (double* pixel_ptr = pixel)
                {
                    pixel_ptr[(uint)ColorIndex.R] = value;
                }
            }
        }

        /// <summary>緑輝度</summary>
        public unsafe double G {
            get {
                fixed (double* pixel_ptr = pixel)
                {
                    return pixel_ptr[(uint)ColorIndex.G];
                }
            }
            set {
                fixed (double* pixel_ptr = pixel)
                {
                    pixel_ptr[(uint)ColorIndex.G] = value;
                }
            }
        }

        /// <summary>青輝度</summary>
        public unsafe double B {
            get {
                fixed (double* pixel_ptr = pixel)
                {
                    return pixel_ptr[(uint)ColorIndex.B];
                }
            }
            set {
                fixed (double* pixel_ptr = pixel)
                {
                    pixel_ptr[(uint)ColorIndex.B] = value;
                }
            }
        }

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

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

        /// <summary>ノルム</summary>
        public double Norm => R * R + G * G + B * B + A * A;

        /// <summary>ノルム(透明度を除く)</summary>
        public double NormWithoutAlpha => R * R + G * G + B * B;

        #endregion


        #region Indexer

        /// <summary>インデクサ</summary>
        /// <param name="color_index">チャネル</param>
        public unsafe double this[ColorIndex color_index] {
            get {
                fixed (double* pixel_ptr = pixel)
                {
                    return pixel_ptr[(uint)color_index];
                }
            }
            set {
                fixed (double* pixel_ptr = pixel)
                {
                    pixel_ptr[(uint)color_index] = value;
                }
            }
        }

        #endregion

        #region Static Function

        /// <summary>アルファブレンデング</summary>
        /// <param name="back">背景色</param>
        /// <param name="over">重層色</param>
        public static RGB AlphaBlend(RGB back, RGB over) {
            if(back.A == 0)
                return over;
            if(over.A == 0)
                return back;

            RGB ret = new RGB();
            double opacity;

            ret.A = 1 - (1 - back.A) * (1 - over.A);
            opacity = ret.A - over.A;

            ret.R = (back.R * opacity + over.R * over.A) / ret.A;
            ret.G = (back.G * opacity + over.G * over.A) / ret.A;
            ret.B = (back.B * opacity + over.B * over.A) / ret.A;

            return ret;
        }

        /// <summary>加法混色</summary>
        /// <param name="back">背景色</param>
        /// <param name="over">重層色</param>
        public static RGB AddtiveBlend(RGB back, RGB over) {
            if(back.A == 0)
                return over;
            if(over.A == 0)
                return back;

            RGB ret = new RGB();

            ret.A = 1 - (1 - back.A) * (1 - over.A);

            ret.R = (back.R * back.A + over.R * over.A) / ret.A;
            ret.G = (back.G * back.A + over.G * over.A) / ret.A;
            ret.B = (back.B * back.A + over.B * over.A) / ret.A;

            return ret;
        }

        /// <summary>減法混色</summary>
        /// <param name="back">背景色</param>
        /// <param name="over">重層色</param>
        public static RGB SubtractiveBlend(RGB back, RGB over) {
            if(back.A == 0)
                return over;
            if(over.A == 0)
                return back;

            RGB ret = new RGB();

            ret.A = 1 - (1 - back.A) * (1 - over.A);

            ret.R = (back.R * back.A - over.R * over.A) / ret.A;
            ret.G = (back.G * back.A - over.G * over.A) / ret.A;
            ret.B = (back.B * back.A - over.B * over.A) / ret.A;

            return ret;
        }

        /// <summary>色距離</summary>
        public static double Distance(RGB cr1, RGB cr2) {
            return Math.Sqrt(SquaDistance(cr1, cr2));
        }

        /// <summary>色距離2乗</summary>
        public static double SquaDistance(RGB cr1, RGB cr2) {
            double dr = cr1.R * cr1.A - cr2.R * cr2.A, dg = cr1.G * cr1.A - cr2.G * cr2.A, db = cr1.B * cr1.A - cr2.B * cr2.A;

            return dr * dr + dg * dg + db * db;
        }

        /// <summary>色距離(透明度を除く)</summary>
        public static double DistanceWithoutAlpha(RGB c1, RGB c2) {
            return Math.Sqrt(SquaDistanceWithoutAlpha(c1, c2));
        }

        /// <summary>色距離2乗(透明度を除く)</summary>
        public static double SquaDistanceWithoutAlpha(RGB c1, RGB c2) {
            double dr = c1.R - c2.R, dg = c1.G - c2.G, db = c1.B - c2.B;

            return dr * dr + dg * dg + db * db;
        }

        #endregion


        #region Operator

        /// <summary>和</summary>
        public static RGB operator +(RGB cr1, RGB cr2) {
            return new RGB(cr1.R + cr2.R, cr1.G + cr2.G, cr1.B + cr2.B, cr1.A + cr2.A);
        }

        /// <summary>差</summary>
        public static RGB operator -(RGB cr1, RGB cr2) {
            return new RGB(cr1.R - cr2.R, cr1.G - cr2.G, cr1.B - cr2.B, cr1.A - cr2.A);
        }

        /// <summary>スカラー倍</summary>
        public static RGB operator *(RGB cr, double s) {
            return new RGB(cr.R * s, cr.G * s, cr.B * s, cr.A * s);
        }

        /// <summary>スカラー倍</summary>
        public static RGB operator *(double s, RGB cr) {
            return cr * s;
        }

        /// <summary>スカラー逆数倍</summary>
        public static RGB operator /(RGB cr, double s) {
            return cr * (1 / s);
        }

        /// <summary>等しいか判定</summary>
        public static bool operator ==(RGB cr1, RGB cr2) {
            return cr1.R == cr2.R && cr1.G == cr2.G && cr1.B == cr2.B && cr1.A == cr2.A;
        }

        /// <summary>異なるか判定</summary>
        public static bool operator !=(RGB cr1, RGB cr2) {
            return !(cr1 == cr2);
        }

        #endregion


        #region Compare

        /// <summary>ハッシュ値</summary>
        public override int GetHashCode() {
            return R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
        }

        /// <summary>等しいか判定</summary>
        public override bool Equals(object obj) {
            return obj is RGB ? (RGB)obj == this : false;
        }

        #endregion


        #region IFormattable

        /// <summary>文字列化</summary>
        public override string ToString() {
            return $"{R},{G},{B},{A}";
        }

        /// <summary>文字列化</summary>
        public string ToString(string format) {
            return $"{R.ToString(format)},{G.ToString(format)},{B.ToString(format)},{A.ToString(format)}";
        }

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

        #endregion


        #region Const

        /// <summary>0</summary>
        public static RGB Zero => new RGB();

        #endregion
    }
    
    /// <summary>RGB表色系クラス</summary>
    /// <typeparam name="ChannelType">チャネル型</typeparam>
    public interface IRGBChannelColor<ChannelType> where ChannelType : struct{
        
        /// <summary>赤輝度</summary>
        ChannelType R { get; set; }

        /// <summary>緑輝度</summary>
        ChannelType G { get; set; }

        /// <summary>青輝度</summary>
        ChannelType B { get; set; }

        /// <summary>透明度</summary>
        ChannelType A { get; set; }

        /// <summary>チャネルインデックス</summary>
        ChannelType this[ColorIndex color_index] { get; set; }
    }

    /// <summary>チャネルインデックス</summary>
    public enum ColorIndex : uint {
        /// <summary>赤輝度</summary>
        R = 0,
        /// <summary>緑輝度</summary>
        G = 1,
        /// <summary>青輝度</summary>
        B = 2,
        /// <summary>透明度</summary>
        A = 3
    }
}

関連項目
HSV空間

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