第13回 HSB表現

ピクセルの色を表すには、いままで使っていたRGBの値を使う方法のほかに、色相、彩度、明度の3つの情報を使う方法 (HSB表現) がある。 なお、明度をValueという値であらわし、HSV表現といういいかたをすることも多い。

これを下のような図で表わしたものを色立体とよび、この3つの座標で表わされる空間を色空間という。


これを真上から見ると


のようになる。この円の中で回転するような色変換をすれば、色合いを変えることができる。
例えば「時計回りに120度回す」という変換なら、赤は緑、緑は青、青は赤に変わる。

今回のプログラムの最終的な機能

プログラムを実行すると実行画面の左側に元画像が表示される。
さらに実行画面上でクリックすると、dataフォルダに の7つの画像が作られる。

1_円追加.png (1キーで表示)


2色相120°回転.jpg (2キーで表示)
(それぞれマゼンタ・緑だった花と葉の色が黄・青に変わる)


2色相240°回転.jpg (3キーで表示)
(それぞれマゼンタ・緑だった花と葉の色がシアン・赤に変わる)


3彩度下げ.jpg (4キーで表示)
(色がせる)


3彩度上げ.jpg (5キーで表示)
(色が鮮やかになる)


3明度下げ.jpg (6キーで表示)
(暗くなる)


3明度上げ.jpg (7キーで表示)
(明るくなる)

色立体 (上から) の追加

概要

上から見た色立体は、彩度は中心から離れるにしたがって高くなり、色相は中心から見た角度に応じた値になる。
Processingでは、colorMode関数で色の指定のしかたを変えることができる。

colorMode(HSB, 360, 100, 100);

を実行すると、色相を0~360, 彩度と明度を0~100の値で指定して色を作れるようになる。つまり、「color c = color(0, 100, 100)」で赤、「color c = color(120, 100, 50)」で暗い緑などの色ができるようになる。
ここでは色立体を真上から見た状態の円を描きたいので、明度 (第3引数) は常に100にして、参照点と円の中心を結ぶ線の角度から色相を、その長さから彩度を決める。

なお、通常の0~255のR, G, Bの値での指定方法に戻すには以下のものを実行すればよい。

colorMode(RGB, 255, 255, 255);

課題 1

ベースのプログラム (クリックしても元画像の右に黒い正方形が追加された画像が作られるだけ)
// 出力用画像の変数
PImage[] img = new PImage[8];

// 出力ファイル名
String[] fName = {
  "1_円追加", "2色相120°回転", "2色相240°回転", 
  "3彩度下げ", "3彩度上げ", "3明度下げ", "3明度上げ"
};

int w, h;

void setup() {
  size(700, 300);
  img[0] = loadImage("元.jpg");
  w = img[0].width;
  h = img[0].height;
  background(0);
  image(img[0], 0, 0, width*w/(w+h), height);
}

// 元画像の右に色の円を追加する
void addColorCircle() {
  // 円の直径、つまり元画像の高さ分だけ横幅を広げたサイズで画像1を作る
  img[1] = createImage(w+h, h, ARGB);
  colorMode(HSB, 360, 100, 100); // 色指定モードをHSBに変更
  int cx=w+h/2; // 円の中心のx座標
  int cy=h/2;   // 円の中心のy座標
  int r=h/2;    // 円の半径
  for (int y=0; y<h; y++) {
    for (int x=0; x<w+h; x++) {
      color c=color(0, 0, 0); // 画像1の(x, y)に設定する色
      // そこが元画像の範囲内ならその色を取得
      if (x<w) {
        c = img[0].pixels[x+y*w];
      }
      // 元画像の右側なら
      else {
        // (x, y)と(cx, cy)の距離
        float d = dist(x, y, cx, cy);
        // 円の外側なら黒に設定
        if (d>r) {
        }
        // 円の内側なら距離に応じた彩度、角度に応じた色相の色にする
        else {
          // (x, y)と(cx, cy)を結ぶ線と水平な線がなす角 → angle
          // angleが負の場合はPI*2だけ増やす
          // angleを「度」に直した値を色相、dに比例して最大100になる値を彩度、明度100の色→c
        }
      }
      img[1].pixels[x+y*(w+h)] = c;
    }
  }
  // これ以降の画像をimg[1]と同じにする
  for (int i=2; i<8; i++) {
    img[i] = img[1].get();
  }
}

void draw() {
}

// n番目の画像の色相をa°回転させる
void changeHue(int n, float a) {
  for (int j=0; j<h; j++) {
    for (int i=0; i<w+h; i++) {
      color c = img[n].pixels[i+j*(w+h)];
      float _h = hue(c);
      float _s = saturation(c);
      float _b = brightness(c);
      // _hにaを加え、360を超えたら360減らす
      img[n].pixels[i+j*(w+h)] = color(_h, _s, _b);
    }
  }
}

// n番目の画像の彩度をa変える
void changeSaturation(int n, float a) {
  for (int j=0; j<h; j++) {
    for (int i=0; i<w+h; i++) {
      color c = img[n].pixels[i+j*(w+h)];
      float _h = hue(c);
      float _s = saturation(c);
      float _b = brightness(c);
      // _sにaを加える
      img[n].pixels[i+j*(w+h)] = color(_h, _s, _b);
    }
  }
}

// n番目の画像の明度をa変える
void changeBrightness(int n, float a) {
  for (int j=0; j<h; j++) {
    for (int i=0; i<w+h; i++) {
      color c = img[n].pixels[i+j*(w+h)];
      float _h = hue(c);
      float _s = saturation(c);
      float _b = brightness(c);
      // _bにaを加える
      img[n].pixels[i+j*(w+h)] = color(_h, _s, _b);
    }
  }
}

// 処理を実行
void mousePressed() {
  addColorCircle();
  changeHue(2, 120);
  changeHue(3, 240);
  changeSaturation(4, -50);
  changeSaturation(5, 50);
  changeBrightness(6, -50);
  changeBrightness(7, 50);
  for (int i=1; i<=fName.length; i++) {
    img[i].save("data/" + fName[i-1] + ".jpg");
  }
  println("完了");
}

// 押したキーに応じて対応する画像を表示
void keyPressed() {
  int k = key-'0';
  if (k>=0 && k<8) {
    background(0);
    if (k==0) {
      image(img[k], 0, 0, width*w/(w+h), height);
    } else {
      image(img[k], 0, 0, width, height);
    }
  }
}
  1. Processingのエディタに上のサンプルプログラムのコードをコピー&ペーストする。
  2. 「img13」という名前で保存する。
  3. 適当に画像検索してサンプル用の画像を用意し、「元.jpg」という名前で保存する。
    • 赤・緑・青・黄・シアン・マゼンタのいずれかの色に近い色を含むものにする。
    • (近いかどうかはこの図を基準に決める。一般にいう「青空」は青ではなく、「真っ赤な炎」は赤とはかなり異なる)

  4. 「元.jpg」をProcessingのエディタにドラッグ&ドロップする。
  5. addColorCircle関数の「// (x, y)と(cx, cy)を結ぶ線と水平な線がなす角 → angle」の下に以下のコードを追加する。
  6. (atan2関数は縦・横の相対座標から角度を求める関数。第2回で既出)
    float angle = atan2(y-cy, x-cx);

  7. 「// angleが負の場合はPI*2だけ増やす」の下に、対応するコードを追加する。
  8. (atan2関数で得られる値の範囲は-PI~PI。色相の値として使えるのは0~360なので、ここで負にならないようにしておく)
    (if文で条件分岐してもよいし、第6回のときのように条件演算子 (「?」と「:」を使った書式) を使ってもよい)

  9. 「// angleを「度」に直した値を色相、dに比例して最大100になる値を彩度、明度100の色→c」の下に、対応するコードを追加する。
  10. (angleはラジアン単位の値。「度」にするにはdegrees関数を使う。単に「180をかけてPIで割る」でもよい)
    (円の半径はr。中心からの距離 d は 0~r の値をとるので、100.0に d/r をかければ 0~100 の値になる)

  11. 実行して画面をクリックする。
  12. (「完了」が表示されたあとでキーボードの1キーを押すと、左側はそのままで、右側に色の円が追加される)
    (この円の1時、3時、5時、7時、9時、11時の端の部分の色が「純粋な」マゼンタ、赤、黄、緑、シアン、青になる)
    (この結果にならない場合はそのまま次に進まないこと。この画像を元に色相の変更などの処理を行う)
  • この段階でdataフォルダの出力画像「1_円追加.jpg」だけが本来の状態になっている。
  • うまくいかない場合は、最終的なaddColorCircle関数とコードを見比べる。

色相の変更

概要

色相の変更は色空間では回転に相当する。
時計回りに120°回転すると、赤は緑、緑は青、青は赤に変わる。


時計回りに240°、つまり反時計回りに120°回転すると、赤は青、青は緑、緑は赤に変わる。

課題 2

  1. changeHue関数の「// _hにaを加え、360を超えたら360減らす」の下に、対応するコードを追加する。
  2. (2段階に分けてもよいし、条件演算子を使って1行で書いてもよい)
    (このコードで扱うのは変数「_h」と「a」だけ。「h」(元画像の高さ) と間違えないように注意)

  3. 実行し、画面をクリックしてコンソールに「完了」が表示されたあとでキーボードの1, 2, 3キーを順に押す。
  4. (右側の円は反時計方向に120°ずつ回転するように見えるはず。それぞれのピクセルの色が色空間で時計回りに120°回転すると、結果的に色の円は反対にまわっているかのように見える)
    (元画像で赤・緑・青・黄・シアン・マゼンタに近い色だった部分がどうなったかを確認すること)

    1キー (花はマゼンタ、葉は緑)


    2キー (マゼンタ→黄、緑 → 青)


    3キー (マゼンタ→シアン、緑 → 赤)


    右の円がこうならない場合は、次に進まずにコードを確認すること。
  5. この段階でdataフォルダの出力画像のうち「2色相120°回転.jpg」「2色相240°回転.jpg」も然るべきものになる。
  6. うまくいかない場合は、最終的なchangeHue関数とコードを見比べる。

彩度・明度の変更

概要

彩度の値を小さくすることは、色空間では内側への移動にあたる。結果として色褪せた状態になる。


彩度の値を大きくすることはその逆で、色が鮮やかになる。


明度を上げ下げすることは色空間での上下方向の移動にあたる。上げると明るく、下げると暗くなる。

課題 3

  1. changeSaturation関数の「// _sにaを加える」の下に、対応するコードを追加する。
  2. (_sが100を超えたり、0未満になった場合の例外処理は考えなくてよい。色をつくるときは、_sが100を超えていたら100, 0未満なら0として扱われる)

  3. 実行し、画面をクリックしてコンソールに「完了」が表示されたあとでキーボードの1と4, 1と5キーをそれぞれ交互に押す。
  4. 1キー (これを基準にして変化を確認する)


    4キー (色褪せる)


    5キー (鮮やかになる)


  5. changeBrightness関数の「// _bにaを加える」の下に、対応するコードを追加する。
  6. (こちらも例外処理は考えなくてよい)

  7. 実行し、画面をクリックしてコンソールに「完了」が表示されたあとでキーボードの1と6, 1と7キーをそれぞれ交互に押す。
  8. 1キー (これを基準にして変化を確認する)


    6キー (暗くなる)


    7キー (明るくなる)


  9. この段階でdataフォルダの出力画像のうち「3彩度下げ.jpg」「3彩度上げ.jpg」「3明度下げ.jpg」「3明度上げ.jpg」も然るべきものになる。
  10. うまくいかない場合は、最終的なchangeSaturation関数、changeBrightness関数とコードを見比べる。

提出

小テスト予告

次回の授業の初めに今回の内容についての小テストを行う。

戻る

inserted by FC2 system