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}) \)
減法混色
背景色の輝度と透明度\(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}) \)
アルファブレンデング
背景色の輝度と透明度\(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}) \)
ソースコード
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空間