CGプログラミング演習
4 点を定義する

本日の講義では、ここまで苦労して学んできた for 文を活用し、ピクセルを解析して、独自のフィルタをつくることを学びます。

1 アニメーションとプログラムの構造

  • ここまで静止画像の生成を中心にプログラムしてきました。
  • ここで Processing におけるアニメーションの描画を学びます。

1.1 最初の例 増殖する円

  • まずは実例を確認してみましょう。

増殖する円

// アニメーションを開始する準備
void setup()
{
  // キャンバスサイズを設定する
  size(300, 300);
  
  // フレームレートの設定
  frameRate(12);
  
  // 描画結果をアンチエイリアシングする。
  smooth();
  
  // 背景を黒く塗りつぶす
  background(0);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // 円の直径をランダムな数とする
  float diameter = random(100);

  // 線を描画しない
  noStroke();
  
  // 塗りぶつしの色、不透明度をランダムに定義
  fill(random(255), random(255), random(255), random(255));
  
  // 円の描画
  ellipse(random(width), random(height), diameter, diameter);
}
  • setup(), draw()など、見慣れない単語が、プログラムの中にいくつか存在しています。
  • これらは、Processing でアニメーションを描画するために用意された特別な機能を利用する時に必要なものです。

1.2 アニメーションの構造と考え方

1.2.1 アニメーションの構造

  • setup:初期化関数
    • プログラムの最初に、1回だけ実行される処理を記述
    • アニメーションの前準備
      • setup 関数の中で行われることの多い処理
        • size:キャンバスサイズの設定
        • colorMode:カラーモードの設定
        • frameRate:画面のフレームレート(書き換え速度)の設定
  • draw:メインループ関数
    • プログラムが終了するか、noLoop()関数が呼ばれるまでは、プログラムを実行し続ける
    • ループの中で図形の場所や色、形を操作してアニメーションにする
    • 画面の書き換え頻度はframerate()関数で設定する

1.2.2 アニメーション作成の考え方

  • setup関数で全体に共通の設定を初期化
  • draw関数を繰り返す
  • 背景の描画
  • 図形を描く
  • パラメータ(場所、色など)を微妙に変更

1.3 2番目の例 動く円

  • 2つめの例として、移動する円を描画してみます。

移動する円

// 円の座標位置を把握しておくための変数を宣言
int x, y;

// アニメーションを開始する準備
void setup()
{
  // キャンバスサイズを設定する
  size(300, 300);
  
  // フレームレートの設定
  frameRate(24);
  
  // 描画結果をアンチエイリアシングする。
  smooth();
  
  // 背景を黒く塗りつぶす
  background(0);

  // x, y 座標を 0 から開始する
  x = 0;
  y = 0;
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // 背景をフレームが更新される度に黒く塗りつぶす。
  background(0);

  // 円を描画する。
  ellipse(x, y, 50, 50);

  // 円の座標位置を 1ピクセルずつずらす
  x++;
  y++;
}

2 画像のピクセルを読み取って加工する

  • ここまで学んだ for 文による x, y 軸方向に進行する繰り返しの技術と、アニメーション用のプログラム構造を利用して、画像の加工に進みます。

2.1 画像のピクセルをそのままキャンバスのピクセルにうつしかえる

  • 作業を進めるにあたって、あらかじめ下記の画像を、プロジェクトフォルダの中に保存しておいてください。
  • ファイル名は、base_large.jpg です。

加工する元の画像(450x600ピクセル)

// プログラムで取り扱えるように、画像を読み込む
// image という変数を用意し、画像の情報を格納する。
PImage img;

// アニメーションを開始する準備
void setup()
{
  // 画像の内容を読み込んでおく。
  img = loadImage("base_large.jpg");

  // 画像の中のピクセルをロードしておく。
  img.loadPixels();

  // 画像の大きさをもとにして、キャンバスサイズを指定
  size(img.width, img.height);
  
  // フレームレートの設定
  frameRate(12);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // 背景を黒く塗りつぶす
  background(0);

  // キャンバスのピクセルをロードする *キャンバスのピクセルをさわる時のおまじない。
  loadPixels();

  // 変数を用意する。
  int x, y, pos;

  for (y = 0; y < img.height; y++)
  {
    for (x = 0; x < img.width; x++)
    {
      // 現在のピクセルの位置を取得する。
      pos = (y * width) + x;

      // 画像のピクセルを、そのままキャンバスのピクセルに代入する(コピーする)
      pixels[pos] = img.pixels[pos];
    }
  }

  // キャンバスのピクセルを更新し、変更内容をキャンバスの描画に反映する。
  updatePixels();
}

2.2 画像のピクセルを読み取り、point などの描画関数を用いて描画する

ピクセルをもとに、ドット画像を point で描画した例

// プログラムで取り扱えるように、画像を読み込む
// image という変数を用意し、画像の情報を格納する。
PImage img;

// アニメーションを開始する準備
void setup()
{
  // 画像の内容を読み込んでおく。
  img = loadImage("base_large.jpg");

  // 画像の中のピクセルをロードしておく。
  img.loadPixels();

  // 画像の大きさをもとにして、キャンバスサイズを指定
  size(img.width, img.height);
  
  // フレームレートの設定
  frameRate(12);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // 背景を黒く塗りつぶす
  background(0);

  // キャンバスのピクセルをロードする。
  loadPixels();

  // 変数を用意する。
  int x, y, pos;

  for (y = 0; y < img.height; y = y + 2)
  {
    for (x = 0; x < img.width; x = x + 2)
    {
      // 現在のピクセルの位置を取得する。
      pos = (y * width) + x;

      // 現在のピクセル位置の色情報を取得する。      
      color c = img.get(x, y);

      // 現在のピクセル位置の色を、r, g, b それぞれ取得する。
      float r = red(c);
      float g = green(c);
      float b = blue(c);

      // ピクセルの色情報に基づいて、塗りつぶしの色を決定する。
      stroke(r, g, b, 200);
      
      // ピクセル情報をもとにした描画
      point(x, y);
    }
  }
}

2.2.1 クイズ

  • 上のプログラムで使用している変数 r, g, b の値を加工して、描画結果の色を反転するにはどうすればよいか考えてみてください。
  • ヒント:r, g, b の値の最大値は、何も設定していないので 255 です。

2.3 独自の画像効果(フィルター)を作る

  • ピクセルをひとつひとつ走査することができれば、独自の画像効果を簡単につくりだすことが可能です。

2.3.1 プログラムのアレンジ

  • 上のプログラムのピクセルを走査している箇所を変更すると、もとの画像にさまざまな視覚的効果を付け加えることができます。
  for (y = 0; y < img.height; y = y + 10)   // y++ を y = y + 10 に変更 = 10ピクセル刻みで増加
  {
    for (x = 0; x < img.width; x = x + 10)  // x++ を y = y + 10 に変更 = 10ピクセル刻みで増加
    {
      // 現在のピクセルの位置を取得する。
      pos = (y * width) + x;

      // 現在のピクセル位置の色情報を取得する。      
      color c = img.get(x, y);

      // 現在のピクセル位置の色を、r, g, b それぞれ取得する。
      float r = red(c);
      float g = green(c);
      float b = blue(c);

      // ピクセルの色情報に基づいて、塗りつぶしの色を決定する。
      fill(r, g, b);
      
      // 中心座標が -5 〜 5 の範囲でランダムにズレる円を描画する。
      ellipse(random(x-5, x+5), random(y-5, y+5), 20, 20);
    }
  }

中心座標が -5 〜 5 の範囲でランダムにズレる円で画像を描画する。

2.3.2 演習

  • 上のプログラムをさらにアレンジして、自分なりの画像効果をつくってみましょう。
  • ピクセルのかわりになにがしかの描画を行う方法、色を加工する方法、不透明度を調整する方法……いろいろな方法が考えられます。

どのようなおもしろい効果が得られるでしょうか?

3 カメラ映像のピクセルを読み取って加工する

  • アニメーションの考え方と画像のピクセル読み取りの考え方を組みあわせることで、カメラ映像の加工が可能になります。

3.1 カメラ映像のピクセルをそのままキャンバスのピクセルにうつしかえる

  • カメラ映像を扱う場合も、プログラムの基本構造は、アニメーションと同様、setup() と、draw() からなります。
  • さきほどと同様に、カメラ映像のピクセルをそのままキャンバスに移し替えるところからはじめてみましょう。

カメラ画像をそのまま取り込む。

// カメラを扱うためのライブラリをインポートする *おまじない
import processing.video.*;

// カメラオブジェクトを用意する(プログラミングにおけるカメラ)
Capture Camera;

// アニメーションを開始する準備
void setup()
{
  // キャンバスサイズを設定する
  size(320, 240);

  // 読み取るカメラ映像のサイズ/フレームレートを設定する
  Camera = new Capture(this, width, height, 12);

  // フレームレートを設定
  frameRate(12);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // カメラのピクセルをロードする(取り出す)。
  Camera.loadPixels();

  // キャンバスのピクセルをロードする。
  loadPixels();

  // 変数の宣言
  int x, y, pos;

  // ロードしたカメラ映像のピクセルを、すべて読み取っていく。
  for (y = 0; y < height; y++)
  {
    for (x = 0; x < width; x++)
    {
      // 現在のピクセルの位置を取得する。
      pos = (y * width) + x;

      // カメラ映像のピクセルを、そのままキャンバスのピクセルに代入する(コピーする)
      pixels[pos] = Camera.pixels[pos];
    }
  }

  // キャンバスのピクセルを更新し、変更内容をキャンバスの描画に反映する。
  updatePixels();
}

// カメラ映像のイベントハンドラ *カメラ映像を利用する場合のおまじない
void captureEvent(Capture camera)
{
  camera.read();
}
  • カメラ映像を扱うための見慣れない命令が増えましたが、根本的な部分は、for 文によるピクセルの走査をしている箇所です。
  • その処理内容は、画像を扱った時とほぼ同じです。
  • 違うのは、もとの画像が、カメラから取得したものにかわったことで、常に変化するようになった点です。

3.2 カメラ映像のピクセルを読み取り、point などの描画関数を用いて描画する

  • 先ほどと同様、カメラ映像のピクセルを point などの描画関数を用いて再現します。

カメラ画像を point 関数を使用して描画する。これは y 軸方向に 1ピクセルおきに描画したもの。

// カメラを扱うためのライブラリをインポートする *おまじない
import processing.video.*;

// カメラオブジェクトを用意する(プログラミングにおけるカメラ)
Capture Camera;

// アニメーションを開始する準備
void setup()
{
  // キャンバスサイズを設定する
  size(320, 240);

  // 読み取るカメラ映像のサイズ/フレームレートを設定する
  Camera = new Capture(this, width, height, 12);

  // フレームレートを設定
  frameRate(12);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  background(0);
  
  // カメラのピクセルをロードする(取り出す)。
  Camera.loadPixels();

  // 変数の宣言
  int x, y, pos;

  // 塗りつぶしをしない。
  noFill();

  // 背景を黒く塗りつぶす
  background(0);

  // ロードしたカメラ映像のピクセルを、すべて読み取っていく。
  for (y = 0; y < height; y = y + 2)
  {
    for (x = 0; x < width; x++)
    {
      // ピクセルの位置を決定する。
      pos = (y * width) + x;

      // color 型の変数 c を用意し、読み取ったピクセルの色情報を保存しておく。
      color c = Camera.pixels[pos];
      
      // ピクセルの色情報を変数に代入しておく。
      float r = red(c);
      float g = green(c);
      float b = blue(c);

      // ピクセルの色情報に基づいて、線の色を決定し、point を描画する
      stroke(r, g, b);
      point(x, y);
    }
  }
}

// カメラ映像のイベントハンドラ *カメラ映像を利用する場合のおまじない
void captureEvent(Capture camera)
{
  camera.read();
}

3.3 カメラ映像のピクセル情報を加工して描画する

  • ここまでくれば、さきほどの画像の時と同様の方法が使えるようになります!

カメラ映像を円で描画した例。

import processing.video.*;

Capture Camera;

// アニメーションを開始する準備
void setup()
{
  // キャンバスサイズを設定する
  size(320, 240);
  
  // カラーモードを HSB に設定する。
  colorMode(HSB, 100, 100, 100, 100);

  // 描画結果をアンチエイリアシングする。
  smooth();

  // 読み取るカメラ映像のサイズ/フレームレートを設定する
  Camera = new Capture(this, width, height, 12);

  // フレームレートを設定
  frameRate(12);
}

// アニメーションで描画される内容の定義(1フレームごとに行う処理)
void draw()
{
  // 背景を黒く塗りつぶす
  background(0);

  // 塗りつぶしをしない。
  noFill();
  
  // 線幅を 2ピクセルにする。
  strokeWeight(2);

  // カメラのピクセルをロードする(取り出す)。
  Camera.loadPixels();

  // 変数の宣言
  int x, y, pos;

  for (y = 0; y < height; y = y + 10)
  {
    for (x = 0; x < width; x = x + 10)
    {
      // ピクセルの位置を決定する。
      pos = (y * width) + x;
      
      // color 型の変数 c を用意し、読み取ったピクセルの色情報を保存しておく。
      color c = Camera.pixels[pos];
      
      // 色相・彩度・明度をそれぞれ取得する。
      float h = hue(c);
      float s = saturation(c);
      
      // ただし明度に関しては、40以内に範囲をせばめる。
      // brightness は、setup 関数の中で 100 段階になっている。
      float b = 40 * brightness(c) / 100;

      // 色相と彩度を線の色に反映する。
      stroke(h, s, 100, 100);
      
      // 塗りつぶさない。
      noFill();
      
      // 円を描く。円の大きさをさきほど設定した明度とする。
      ellipse(x, y, b, b);
    }
  }
}

// カメラ映像のイベントハンドラ *カメラ映像を利用する場合のおまじない
void captureEvent(Capture camera)
{
  camera.read();
}

4 演習

  • カメラ映像を独自に加工するフィルターをつくってみてください。