HSV カラー

HSV カラー体系は色を色相(Hue)、彩度(Saturation)、値(Value) [1] の三つの成分に分解して表現する方法です。たとえば下図のように 10 個の四角形を描くために、それぞれ異なる「明るい」色を算出する、といった場合に便利です。

HSV サンプル

このような場合に RGB 体系、つまり三つの原色に分解して、それぞれの成分の大きさを指定する方法では、RGB それぞれどの値にするのが適切なのか判断するのは少々面倒そうです。

HSV サークル

下図では HSV の色相と彩度の関係を円盤の形で示しています。ある特定の色(下図で円盤右上に取り出した黄色)を示そうとした場合、色相を角度で、彩度を原点からの距離で示します。角は円の中央から右方向を基準として、左周りに数えています。

HSV circle

乱暴な言い方をすると、色相とは色の方向性(何色っぽいか、という傾向)、彩度とはその色が含まれる度合いのことです。最も円の端にあるのが、最も濃い色なのですから、上にあった「10の濃い色を選ぶ」には「円盤の最外周を 10 分割した場所にある色を選べばよい」ことになります。

ところで、上の図では値(Value、または明度 Brightness)を常に最大にしています。値と色相・彩度の関係を理解しやすいように、H, S, V の数値をつけた色見本を用意しました。特に左下の値がゼロのブロックが全て黒となることに注目すれば、値が何を意味しているのか分かると思います。この色見本を表示するプログラムもつけておきますので参考にして下さい。
色見本, hsvchart.c

HSV 系で色を指定することができる関数を作る

HSV 系で色を指定することができる関数をこの演習用に用意してみました。

hgcolor HgHSV(double h, double s, double v)

h, s, v はいずれも実数型。それぞれ hue, satulation, value を意味し、いずれも 0.0〜1.0 の数値でそれぞれの度合いを設定します。 ただし、HgHSV 関数で直接描画色などを指定することはできません。 Handy Graphic で色を設定する関数は HgSetColor, HgSetFillColor 関数なので、そこに与える値を HgHSV 関数で作る、といった使い方をします。

以下に具体的に緑色の長方形を描く例を示します。(つまり色相環上で0.333方向にある緑を指定し、発色が最大となるよう指定している。)

  h=0.333; s=1.0; v=1.0; 
  HgSetFillColor(HgHSV(h, 1.0, 1.0));  
  HgBoxFill(100.0, 100.0, 50.0, 50.0);

newhsvcolor 関数を含むプログラム

HSV カラーで説明に用いた図をもう一度示します。

HSV サンプル

この描画は、上に示した HgHSV 関数を使って実現しています。以下に HgHSV 関数そのものの定義を含むサンプルプログラムを示しますので参考にしてください。 HSV 系から RGB 系への値の変換は Wikipedia の HSV色空間 の記述を参考にしています。

#include <stdio.h>
#include <math.h>
#include <handy.h>

hgcolor HgHSV(double h, double s, double v)
{
    double r = v;
    double g = v;
    double b = v;
    if (s > 0.0) {
        h *= 6.0;
        int i = (int) h;
        double f = h - (double) i;
        switch (i) {
            default:
            case 0:
                g *= 1 - s * (1 - f);
                b *= 1 - s;
                break;
            case 1:
                r *= 1 - s * f;
                b *= 1 - s;
                break;
            case 2:
                r *= 1 - s;
                b *= 1 - s * (1 - f);
                break;
            case 3:
                r *= 1 - s;
                g *= 1 - s * f;
                break;
            case 4:
                r *= 1 - s * (1 - f);
                g *= 1 - s;
                break;
            case 5:
                g *= 1 - s;
                b *= 1 - s * f;
                break;
        }
    }
    
    return HgRGB(r, g, b);
}

int main() {
    int i;
    double h;
    
    HgOpen(460.0, 90.0);
    HgSetFillColor(HG_BLACK);
    HgBoxFill(0.0, 0.0, 460.0, 90.0, 0);
    
    for(i=0;i<10;i++) {
        h=i/10.0; // Hue を決める
        HgSetFillColor(HgHSV(h, 1.0, 1.0)); // S, V は 100%
        HgBoxFill(i*40.0+30.0, 30.0, 30.0, 30.0, 0);
    }
    
    HgGetChar();
    HgClose();
    
    return 0;
}



Yutaka Yasuda (yasuda@bakkers.gr.jp)