ご存知のとおり、光は「光の三原色」すなわち「赤(Red)」「緑(Green)」「青(Blue)」の三色を用いることですべての色を表現することができます(以下このページでは、この表現方法を
例えば24bitカラーの場合、各色の輝度値は8bit、すなわち256階調で、0(まったく光っていない)~255(最も明るい)の間の数値により表されます。ですので、256の3乗色(約1678万色)を表現することができます。以下に例を示します。
(R,G,B)=(255,000,000)->赤色 (R,G,B)=(255,255,000)->黄色 (R,G,B)=(255,128,000)->橙色 (R,G,B)=(128,255,255)->水色
しかし、この表現方法は人間にとってあまり直感的なものではありません。もし「赤が中程度、緑が中程度の半分、青がまったく光っていない色は?」と問われてとっさに「薄暗い橙色」と答えられるでしょうか?あるいは逆に「淡くて薄暗い橙色」という色は創造できても赤緑青がそれぞれどれくらい光っているのかはわからないのではないでしょうか。
つまり人間にわかりやすい表現とは「こういう色合いで、このくらい淡くて、これくらい明るい」といったように「色合い(赤とか橙とか緑といった色の表現)」を基準としたものなのです。ちなみに、この色合いのことを
色相を基準とした色の表現方法に
さて、彩度(色の濃淡)と明るさは単純に数値で表すことができます。それぞれ最小値を「0」、最大値を「255」とすれば、彩度は0が無色で255が原色、明度は0が最も暗くて255が最も明るいと定義することができます。
では、色相はどのように定義すればよいのでしょうか。実は色相環と呼ばれる図があります。色相環は円の真ん中を中心とし、赤を基準(0度)に何度の色かといった具合に、色を定義します。例えば、0度は赤、120度は緑、240度は青といった具合です。.
また、これに彩度を加えると.のようになります(以下このページでは、色相環に彩度を加えた円を
さらに上の図全体を明るくしたり暗くしたりすることで明度を表現します。上の図は最も明るい状態(明度が255)、.は半分程度に明るい状態(明度128)です。ちなみに、真っ暗な状態(明度が0)は円全体が真っ黒です。
説明をする前に、それぞれの表現方法を明確にしておきたいと思います。ここでは、RGB表現でとりうる値を
赤の輝度値 0~255 緑の輝度値 0~255 青の輝度値 0~255
色相 0~360 彩度 0~255 ( 無色 <- -> 原色 ) 明度 0~255 ( 暗い <- -> 明るい )
それではRGB表現からHSV表現へはどのように変換したらよいのでしょうか。もう一度色相円.を見てみましょう。よく見ると赤緑青がちょうど円を三等分した位置に配置されていることがわかります。
さらに輝度値と合わせてよく観察すると
000度 (R,G,B)=(255,000,000) 060度 (R,G,B)=(255,255,000) 120度 (R,G,B)=(000,255,000) 180度 (R,G,B)=(000,255,255) 240度 (R,G,B)=(000,000,255) 300度 (R,G,B)=(255,000,255) 中心 (R,B,B)=(255,255,255)
では、ベクトルによってRGB表現とHSV色相系の関係を明らかにしてみましょう。色相環の中心を原点とするX-Y平面を考えます。また、円の半径は255とします。ここに赤緑青各色の輝度値を表す、原点から伸びる、輝度値を長さとしたベクトルを配置します。また話を単純にするため、まずはいずれかの輝度値が最大(つまり255)である場合を考えます。.
あとは三つのベクトルを足し合わせれば、六角形の色相円(円ではありませんが)上の色相、彩度を算出することができ、色相は足しあわされたベクトルの偏角、彩度は足しあわされたベクトルの大きさとなります。上の図は、各色の輝度値がそれぞれ
(R,G,B)=(255,YYY,ZZZ) (R,G,B)=(XXX,255,ZZZ) (R,G,B)=(XXX,YYY,255)
六角形のままでは格好が悪いので、円になるよう引き伸ばせば、色相円の出来上がりです。
最後に明度ですが、これは各色の輝度値のうち最大のものであると定義します。例えば、.の大きい円は明度が255のもので、小さい円は明度が128のもの、すなわち
(R,G,B)=(128,YYY,ZZZ) (R,G,B)=(XXX,128,ZZZ) (R,G,B)=(XXX,YYY,128)
ここで、円が小さいのは縮小したわけではなく、ベクトルの大きさが小さいからであることに注意してください。この場合、彩度の最大値が128となってしまうので、最大値が255になるよう補正します。
私は美術的な分野(美術やCGなど)についてあまり詳しいわけではないのですが、この分野では色を考える際に色相環を基準とする傾向があるようですので、今回、私も色相環からRGB表現とHSV色相系の関係を説明しました。ですが、数学的に考えるともう少し合理的な説明ができます。
RGB表現で与えられる色の情報はRGBの三色、つまり三次元です。ですから、すべての色は立体、特にRGBの最大値は255なので、各辺の大きさが255である立方体の中の点として表すことができます。
このとき、RGBがすべて255である点から立方体を見る.と、.のようになります。結局、先ほどやった、平面上でベクトルによりあらわした関係は、立体上でベクトルによりあらわされた点を平面上に投影したものだったのです。
実際の計算はベクトルと相性のよい複素平面で行うことにします。そうする理由は、複素数はベクトルのように扱うことができ、さらに C++ では STL クラスである complex とそれに関連する関数で容易に複素数の計算ができるためです(ただし、説明では C++ に関するものは使用していません)。
以下の説明では、複素数を「ベクトル」と言います。また、以下のように接頭辞「r_」はそれが実数であることを、接頭辞「i_」はそれが虚数であることを示し、接頭辞がないものはベクトル(複素数)であるものとします。
r_X 実数 i_X 虚数
real(C): C の実部 imag(C): C の虚部 cos(r_X): r_X のコサイン sin(r_X): r_X のサイン arg(C): C の偏角 abs(C): C の大きさ(長さ) // 以上の関数は実際に C++ でも用意されています。 // ただし、引数にとりうる値や返される値の範囲に注意してください。 // ここでは角度は度数表示、偏角の範囲は 0度~360度 とします。 max(r_X, r_Y, r_Z): r_X, r_Y, r_Z のうち最も大きいもの min(r_X, r_Y, r_Z): r_X, r_Y, r_Z のうち最も小さいもの また、計算するに当たり、各色の単位ベクトルを定義しておきます。 赤 RE = r_cos(000) + i_sin(000) 緑 GE = r_cos(120) + i_sin(120) 青 BE = r_cos(240) + i_sin(240)
与えられた各色の輝度値と求めるべき色相、彩度、明度をそれぞれ
赤 -> r_R 緑 -> r_G 青 -> r_B 色相 -> r_H 彩度 -> r_S 明度 -> r_V
最初に明度(r_V)を求めます。これは r_R,r_G,r_B のうちもっとも大きい値をとれば、それが明度となります。即ち
r_V = r_max(r_R,r_G,r_B)
次に色相を求めます。各色の単位ベクトル RE,GE,BE に輝度値をかけて大きさを持たせ、それらを足し合わせたベクトル C を求めると、その偏角が色相になります。即ち
C = r_R * RE + r_G * GE + r_B * BE r_H = r_arg(C)
最後に彩度を求めます。彩度はベクトル C の大きさとしたいところですが、前述のとおり
1. 最大値が255になるように修正 2. 六角形が円になるように修正
1. については単純に比を考えればよいので、説明は省略します。
2. についても、.のように中心から円周方向に向かって比を考えて引き伸ばせばよいでしょう。
具体的には、0度~60度までを考えます。残りの角度については、0度~60度のところに当てはめて考えてください。
0度~60度の部分ですが、.のように30度回転して考えるとよいでしょう。
こうすれば、直角三角形を利用して実線部分の長さを求めることができます。ここで直角三角形の長さを r_S0 とすれば、r_S0 は
r_S0 = r_cos(h) / r_X ※ r_X は底辺の長さ(= ルート3 / 2)
r_S0 * r_A = 255
r_S = r_A * abs(C)
与えられた色相、彩度、明度と求めるべき各色の輝度値をそれぞれ
色相 -> r_H 彩度 -> r_S 明度 -> r_V 赤 -> r_R 緑 -> r_G 青 -> r_B
1. 最大値が255になるように修正 2. 六角形が円になるように修正
さて、色相 r_H、彩度 r_S であらわされるベクトル C は
C = r_S * r_cos(r_H) + r_S * i_sin(r_H)
r_HR = |r_H - r_arg(RE)| r_HG = |r_H - r_arg(GE)| r_HB = |r_H - r_arg(BE)|
r_min(r_HR, r_HG, r_HB) = r_HR
r_R = r_V
C' = C - r_R * RE
C' = r_G * GE + r_B * BE
r_real(C') = r_G * r_real(GE) + r_B * r_real(BE) r_imag(C') = r_G * r_imag(GE) + r_B * r_imag(BE)
ここで紹介した方法以外にもRGB表現とHSV色相系を関連付ける式はあるようです。
もっとも、私の書いたこのページもHSV色相系の標準的な定義 (*1) を調べたわけではないのでこれが絶対正しいとも言えません。所詮、人が定義したものですので。