---------------------------------------------------------------- 「RGB←→YUV,HLS,HSV」の相互変換計算を行うC#エクスプレッション ---------------------------------------------------------------- 2009.01.24 作成 ■注意事項  ※例によって初心者が書いたプログラムなので、間違いやバグなどもあると思います。    一応色々調べた結果をもとに作ってますが、参考程度と考えてください。    間違ってても責任はとらないぜ!っていうか間違ってたら教えて下さい。m(_ _)m  ※カラーピッカーやカラーパレットで選択したRGB色から、YUV・HLS・HSVでの    色表現数値を計算し、プロパティモニタに表示します。  ※元々は「ルミナンスキー」で使う、明るさ(YUV輝度)の数値を調べるために作ってたものです。    カラーピッカーで色を選んで、その色のYUV輝度を計算し、    それをルミナンスキーの「明るさ」に自動設定させていました。  ※YUVについてはYCbCrとか色々な定義や計算式があるみたいですが、    「とりあえずルミナンスキーで使ってるYUV輝度だけちゃんと計算できればいいや」ってことで    それっぽい計算式を選んでメソッドを作っています。    これだけ見て「これがYUVかー」なんて思っちゃうと致命的な致命傷を負ったうえに    追撃のグランドヴァイパでダメージが加速して裏世界でひっそりと幕を閉じることになりかねないので、    各メソッドのコメントに記載してある参考サイトをしっかり見ることをお勧めします。  ※Windows標準のカラーパレット(ペイントやNiVEが使ってるやつ)は、    「鮮やかさ(HLS彩度)」が0の場合は「色合い(色相)」を強制的に160にするっぽいです。    なんだろう、この謎の挙動。    今回作ったメソッドでは、HLS彩度が0なら色相も0にしてます。    HLS彩度が0なら色相の値に意味はないはずですし。  ※HSVの数値があってるかどうかはGIMP2で確認を行いましたが、    たまに微妙な誤差が生じることもあるようです。 ■使い方  ※「エフェクト→エクスプレッション制御→色」をエフェクトに追加し、    そのエフェクトを右クリックして「エクスプレッションを使用する」を選択。    「高度」にチェックを入れて、「メソッド・フィールド」および「メイン」のタブに    以下のエクスプレッションをコピペ。    ピッカーやカラーテーブルで色を取得すると、そのRGB値から    YUV・HLS・HSVでの数値表現を計算。    また、その結果からRGB値を逆算しています。    計算結果は「プロパティモニタ」タブ内に表示されます。  ※色々な情報を表示させているので、デフォルトの状態だと    「プロパティモニタ」タブに表示している情報が切れてしまいます。    タブの表示枠の下側をドラッグすれば表示領域を広げることができるので、    広げて内容が見えるようにしてください。 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■    「メソッド・フィールド」タブに書く内容 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ // ====================================================================== // Color構造体の値からYUVの数値を計算して返す // // 第二引数の意味は以下の通り //  0・・・Y(YUV輝度)を返す //  1・・・U(青色色差?)を返す //  2・・・V(赤色色差?)を返す // // 返り値の値範囲は、 //  Y(YUV輝度)の場合・・・0.0〜255.0 //  U(青色色差?)の場合・・・-128.0〜127.0 //  V(赤色色差?)の場合・・・-128.0〜127.0 // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html //  http://image-d.isp.jp/commentary/color_cformula/YUV.html //  http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html //  http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E6%98%A0%E5%83%8F%E4%BF%A1%E5%8F%B7 // // なんかYUV系の計算式はたくさんあるけど、Y値とRGBの値範囲が // 0〜255になるのはこの計算式っぽいのでこれを採用してみたら、 // Y値とルミナンスキーの明るさとがうまく一致した。 // // ====================================================================== float getYUVfromColor(Color color, int paramtype) { float ret = 0; switch(paramtype){ case 0: ret = 0.299f * color.R + 0.587f * color.G + 0.114f * color.B; break; case 1: ret = -0.169f * color.R - 0.331f * color.G + 0.500f * color.B; break; case 2: ret = 0.500f * color.R - 0.419f * color.G - 0.081f * color.B; break; } return ret; } // ====================================================================== // Color構造体の値からHLS色空間の色相・HLS彩度・HLS明度を計算して返す // // Color構造体のGetHue(),GetSaturation(),GetBrightness()と // 同じ結果になるはず。 // // 第二引数の意味は以下の通り //  0・・・Hue(色相)を返す //  1・・・Saturation(HLS彩度)を返す //  2・・・Luminance(HLS明度)を返す。 //       ※Luminanceという単語は「輝度」と訳されることが多いから //        YUV輝度と混同しやすいんだよなあ・・・。 //        Lightness説もあるけど英語版Windowsのペイントの //        パレットの「明るさ」は「Lum.」と表記されてるみたいだなー。 //        まあ、オイラは「HLS明度」と呼ぶのでどうでもいいですけど。 // // 返り値の値範囲は、 //  Hue(色相)の場合・・・0.0〜360.0 //  Saturation(HLS彩度)の場合・・・0.0〜1.0 //  Luminance(HLS明度)の場合・・・0.0〜1.0 // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html // http://image-d.isp.jp/commentary/color_cformula/HLS.html // ====================================================================== float getHLSfromColor(Color color, int paramtype) { int max = Math.Max(Math.Max(color.R,color.G),color.B); int min = Math.Min(Math.Min(color.R,color.G),color.B); float hue; float saturation; float luminance = (max + min) / 2.0f / 255.0f ; if(max == min){ hue = 0; saturation = 0; } else{ float d = max - min; if(luminance <= 0.5f){ saturation = d / (max + min); } else{ saturation = d / (510.0f - max - min); } float cr = (max - color.R) / d; float cg = (max - color.G) / d; float cb = (max - color.B) / d; if(max == color.R){ hue = 60.0f * (cb - cg); } else if(max == color.G){ hue = 60.0f * (2.0f + cr - cb); } else{ hue = 60.0f * (4.0f + cg - cr); } if(hue < 0) hue += 360.0f; } float ret = 0; switch(paramtype) { case 0: // Hue(色相) ret = hue; break; case 1: // Saturation(HLS彩度) ret = saturation; break; case 2: // Luminance(HLS明度) ret = luminance; break; } return ret; } // ====================================================================== // Color構造体の値からHSV色空間の色相・HSV彩度・HSV明度を計算して返す // // 第二引数の意味は以下の通り //  0・・・Hue(色相)を返す //  1・・・Saturation(HSV彩度)を返す //  2・・・Value(HSV明度)を返す // // 返り値の値範囲は、 //  Hue(色相)の場合・・・0.0〜360.0 //  Saturation(HSV彩度)の場合・・・0.0〜1.0 //  Value(HSV明度)の場合・・・0.0〜1.0 // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html // http://image-d.isp.jp/commentary/color_cformula/HSV.html // ====================================================================== float getHSVfromColor(Color color, int paramtype) { int max = Math.Max(Math.Max(color.R,color.G),color.B); int min = Math.Min(Math.Min(color.R,color.G),color.B); float hue; float saturation; float value = max / 255.0f; float d = max - min; if( (max == 0) || (d == 0) ){ hue = 0; saturation = 0; } else{ saturation = d / max; float cr = (max - color.R) / d; float cg = (max - color.G) / d; float cb = (max - color.B) / d; if(max == color.R){ hue = 60.0f * (cb - cg); } else if(max == color.G){ hue = 60.0f * (2.0f + cr - cb); } else{ hue = 60.0f * (4.0f + cg - cr); } if(hue < 0) hue += 360.0f; } float ret = 0; switch(paramtype) { case 0: // Hue(色相) ret = hue; break; case 1: // Saturation(HSV彩度) ret = saturation; break; case 2: // Value(HSV明度) ret = value; break; } return ret; } // ====================================================================== // YUVの値からRGB値を計算し、対応するColor構造体を作成して返す。 // // 引数の値範囲は以下の通り //  Y(YUV輝度)・・・0.0〜255.0 //  U(青色色差?)・・・-128.0〜127.0 //  V(赤色色差?)・・・-128.0〜127.0 // // 返り値のColor構造体のアルファ部は255(完全不透明)にする // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html //  http://image-d.isp.jp/commentary/color_cformula/YUV.html //  http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html //  http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E6%98%A0%E5%83%8F%E4%BF%A1%E5%8F%B7 // // ====================================================================== Color getRGBColorFromYUV(float Y, float U, float V) { int R = (int)Math.Round(1.000 * Y + 1.402 * V); int G = (int)Math.Round(1.000 * Y - 0.344 * U - 0.714 * V); int B = (int)Math.Round(1.000 * Y + 1.772 * U); return Color.FromArgb(255,R,G,B); } // ====================================================================== // HLS色空間の色相・HLS彩度・HLS明度の値からRGB値を計算し、 // 対応するColor構造体を作成して返す。 // // 引数の値範囲は以下の通り //  Hue(色相)・・・0.0〜360.0 //  Saturation(HLS彩度)・・・0.0〜1.0 //  Luminance(HLS明度)・・・0.0〜1.0 // // 返り値のColor構造体のアルファ部は255(完全不透明)にする // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html // http://image-d.isp.jp/commentary/color_cformula/HLS.html // ====================================================================== int calcRGBForHLS(float max, float min, float h) { int ret; if(h < 60.0f) ret = (int)Math.Round((min + (max - min) * h / 60.0f) * 255.0f); else if(h < 180.0f) ret = (int)Math.Round(max * 255.0f); else if(h < 240.0f) ret = (int)Math.Round((min + (max - min) * (240.0f - h) / 60.0f) * 255.0f); else ret = (int)Math.Round(min * 255.0f); return ret; } Color getRGBColorFromHLS(float Hue, float Saturation, float Luminance) { float max; if(Luminance <= 0.5f) max = Luminance * (1.0f + Saturation); else max = Luminance * (1.0f - Saturation) + Saturation; float min = 2.0f * Luminance - max; int R = 0; int G = 0; int B = 0; if(Saturation == 0.0f){ R = (int)Math.Round(Luminance * 255.0f); G = (int)Math.Round(Luminance * 255.0f); B = (int)Math.Round(Luminance * 255.0f); } else{ float h = Hue + 120.0f; if(h >= 360.0f) h -= 360.0f; R = calcRGBForHLS(max,min,h); h = Hue; G = calcRGBForHLS(max,min,h); h = Hue - 120.0f; if(h < 0) h += 360; B = calcRGBForHLS(max,min,h); } return Color.FromArgb(255,R,G,B); } // ====================================================================== // HSV色空間の色相・HSV彩度・HSV明度の値からRGB値を計算し、 // 対応するColor構造体を作成して返す。 // // 引数の値範囲は以下の通り //  Hue(色相)・・・0.0〜360.0 //  Saturation(HSV彩度)・・・0.0〜1.0 //  Value(HSV明度)・・・0.0〜1.0 // // 返り値のColor構造体のアルファ部は255(完全不透明)にする // // 参考サイト //  http://www9.plala.or.jp/joochan/rgb-convert.html // http://image-d.isp.jp/commentary/color_cformula/HSV.html // http://www.cs.rit.edu/~ncs/color/t_convert.html // ====================================================================== Color getRGBColorFromHSV(float Hue, float Saturation, float Value) { StringProperty dbg = new StringProperty("デバッグ用","\n"); MonitorProperty.Add(dbg); int R = 0; int G = 0; int B = 0; if(Saturation == 0.0f){ R = (int)Math.Round(Value * 255.0f); G = (int)Math.Round(Value * 255.0f); B = (int)Math.Round(Value * 255.0f); } else{ float h = Hue / 60.0f; int i = (int)Math.Floor(h); float F = h - i; float M = Value * (1.0f - Saturation); float N = Value * (1.0f - Saturation * F); float K = Value * (1.0f - Saturation * (1.0f - F)); dbg.TypeSafeValue += "IFMNK=(" + i + ", " + F + ", " + M + ", " + N + ", " + K + ")\n\n"; switch(i){ case 0: R = (int)Math.Round(Value * 255.0f); G = (int)Math.Round(K * 255.0f); B = (int)Math.Round(M * 255.0f); break; case 1: R = (int)Math.Round(N * 255.0f); G = (int)Math.Round(Value * 255.0f); B = (int)Math.Round(M * 255.0f); break; case 2: R = (int)Math.Round(M * 255.0f); G = (int)Math.Round(Value * 255.0f); B = (int)Math.Round(K * 255.0f); break; case 3: R = (int)Math.Round(M * 255.0f); G = (int)Math.Round(N * 255.0f); B = (int)Math.Round(Value * 255.0f); break; case 4: R = (int)Math.Round(K * 255.0f); G = (int)Math.Round(M * 255.0f); B = (int)Math.Round(Value * 255.0f); break; case 5: R = (int)Math.Round(Value * 255.0f); G = (int)Math.Round(M * 255.0f); B = (int)Math.Round(N * 255.0f); break; } } dbg.TypeSafeValue += "RGB = (" + R + ", " + G + ", " + B + ")\n\n"; return Color.FromArgb(255,R,G,B); } ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■    「メイン」タブに書く内容 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ //============================================================== // 「色」のプロパティ値を取得し、RGB値をプロパティモニタに表示。 //============================================================== StringProperty res = new StringProperty("計算結果",""); MonitorProperty.Add(res); // 「色」プロパティ値の取得 ColorProperty color = (ColorProperty)ExpressionUtils.GetProperty(Property.ThisProperty, "色"); // RGB値 res.TypeSafeValue += "RGB=(" + color.TypeSafeValue.R + ", " + color.TypeSafeValue.G + ", " + color.TypeSafeValue.B + ")\n\n"; //-------------------------------------------------------------- // // YUVの全数値の計算と逆算 // //-------------------------------------------------------------- // とりあえずY値(YUV輝度)だけはfloatのままの数値を表示してみる res.TypeSafeValue += "YUV輝度 = " + getYUVfromColor(color.TypeSafeValue,0) + "\n"; // Y,U,V値の計算 res.TypeSafeValue += "YUV= (" + (int)Math.Round(getYUVfromColor(color.TypeSafeValue,0)) + ", " + (int)Math.Round(getYUVfromColor(color.TypeSafeValue,1)) + ", " + (int)Math.Round(getYUVfromColor(color.TypeSafeValue,2)) + ")\n"; // YUV値からRGB値を逆算してみる Color rgbColorForYUV = getRGBColorFromYUV( getYUVfromColor(color.TypeSafeValue,0), getYUVfromColor(color.TypeSafeValue,1), getYUVfromColor(color.TypeSafeValue,2)); // 逆算したRGB値 res.TypeSafeValue += "YUVから逆算したRGB= (" + rgbColorForYUV.R + ", " + rgbColorForYUV.G + ", " + rgbColorForYUV.B + ")\n\n"; //========================================================== // おまけ //  HLSおよびHSV色空間での色表現数値への変換 // // それぞれの値範囲はアプリケーションによって変わるので、 // ここでは最初に //   色相:0〜360 //   HLS彩度・HSV彩度:0〜100 //   HLS明度・HSV明度:0〜100 // という値範囲で計算した結果を表示し、 // その後、各アプリケーションで使われている値範囲での // 計算結果を表示しています。 //========================================================== res.TypeSafeValue += "以下の表記の意味は次の通り。\n"; res.TypeSafeValue += "色空間名(色相最大値-彩度最大値-明度最大値)=(色相,彩度,明度)\n\n"; //-------------------------------------------------------------- // ■HLS色空間の数値計算 // //   .NETのColor構造体はHLS色空間の色相・彩度・HLS明度を //   返すメソッドを持っているので、それを使って計算してみる。 // //   GetHue()・・・色相の値を0〜360で返す //   GetSaturation()・・・彩度の値を0〜1で返す //   GetBrightness()・・・HLS明度の値を0〜1で返す // //   関数名がH・S・Bのくせに、実際にはHLS色空間の数値を返すって //   どういうことなの・・・? //   AfterEffectsではHSBという表現でHSVを使ってるんだぜ・・・? //   混乱するから、もうHSBって表現はやめたほうがよくね? //-------------------------------------------------------------- // 個人的には色相360段階(360度)、彩度・明度は100段階(100%)ってのが // 一番わかりやすいと思うので、その範囲で表してみる。 res.TypeSafeValue += "HLS(360-100-100)= (" + (int)Math.Round(color.TypeSafeValue.GetHue()) + ", " + (int)Math.Round(color.TypeSafeValue.GetSaturation()*100.0f) + ", " + (int)Math.Round(color.TypeSafeValue.GetBrightness()*100.0f) + ")\n"; //-------------------------------------------------------------- // ■HLS色空間の数値計算 // //   メソッド・フィールドに書いたHLS計算メソッドを使って計算する。 //   Color構造体を用いた場合と同じ結果になるはず。 // //-------------------------------------------------------------- // 個人的には色相360段階(360度)、彩度・明度は100段階(100%)ってのが // 一番わかりやすいと思うので、その範囲で表してみる。 res.TypeSafeValue += "HLSその2(360-100-100)= (" + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,0)) + ", " + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,1)*100.0f) + ", " + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,2)*100.0f) + ")\n"; //-------------------------------------------------------------- // ■HSV色空間の数値計算 // //   メソッド・フィールドに書いたHSV計算メソッドを使って計算する // //-------------------------------------------------------------- // 個人的には色相360段階(360度)、彩度・明度は100段階(100%)ってのが // 一番わかりやすいと思うので、その範囲で表してみる。 res.TypeSafeValue += "HSV(360-100-100)= (" + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,0)) + ", " + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,1)*100.0f) + ", " + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,2)*100.0f) + ")\n\n"; //-------------------------------------------------------------- // // 各アプリケーションにあわせた値範囲で表現 // //   ちなみにPixiaではHSVとHLSの両方が扱える模様。 //-------------------------------------------------------------- // ------------------------------------------------------------------ // Windows標準(ペイントやNiVEで使ってるもの)のカラーパレットは // HLS色空間を使用している。同等の数値範囲でHLS値を表してみる。 // //  色合い:0〜239 //  鮮やかさ:0〜240 //  明るさ:0〜240 // // しかしなんで全部240段階なんだろう・・・。 // ------------------------------------------------------------------ res.TypeSafeValue += "ペイントのHLS(240-240-240)= (" + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,0)/360.0f*240.0f) + ", " + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,1)*240.0f) + ", " + (int)Math.Round(getHLSfromColor(color.TypeSafeValue,2)*240.0f) + ")\n"; // ------------------------------------------------------------------ // GIMPやAfterEffectsなどのカラーパレットはHSV色空間を使用している。 // 同等の数値範囲でHSV値を表してみる。 // //  色相:0〜360 //  彩度:0〜100 //  明度:0〜100 // // AfterEffectsはHSBという表現を使っているが、実際にはHSV。 // ------------------------------------------------------------------ res.TypeSafeValue += "GIMPやAEのHSV(360-100-100)= (" + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,0)) + ", " + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,1)*100.0f) + ", " + (int)Math.Round(getHSVfromColor(color.TypeSafeValue,2)*100.0f) + ")\n\n"; //-------------------------------------------------------------- // // HLS色空間の数値からRGBを逆算する // //-------------------------------------------------------------- Color rgbColorForHLS = getRGBColorFromHLS( getHLSfromColor(color.TypeSafeValue,0), getHLSfromColor(color.TypeSafeValue,1), getHLSfromColor(color.TypeSafeValue,2)); // 逆算したRGB値 res.TypeSafeValue += "HLSから逆算したRGB= (" + rgbColorForHLS.R + ", " + rgbColorForHLS.G + ", " + rgbColorForHLS.B + ")\n"; //-------------------------------------------------------------- // // HSV色空間の数値からRGBを逆算する // //-------------------------------------------------------------- Color rgbColorForHSV = getRGBColorFromHSV( getHSVfromColor(color.TypeSafeValue,0), getHSVfromColor(color.TypeSafeValue,1), getHSVfromColor(color.TypeSafeValue,2)); // 逆算したRGB値 res.TypeSafeValue += "HSVから逆算したRGB= (" + rgbColorForHSV.R + ", " + rgbColorForHSV.G + ", " + rgbColorForHSV.B + ")\n\n";