yoppa.org


immediate bitwave

芸大 – メディアアート・プログラミング I 2017

第7回 Processing実習 3 : Processingで画像データを扱う – 画像の分析・再合成

ProcessingのPImageクラスは、外部のビットマップ画像(Jpeg, GIF、PNGなど)をデータとしてプログラムに読み込むことができます。読み込んだ画像は単に画面に表示するだけでなく、色や明度やサイズを変更して表示することができます。さらには、画像に含まれる全てのピクセルの色情報を読み取り配列に格納することが可能です。そのデータをもとに別の画像を再生成することが可能となり、読み込んだ画像データの色情報をもとにした多彩な表現が可能となります。

今回はProcessingに画像を読み込んで、分析再合成することで、様々な表現の可能性について探っていきます。

スライド資料

サンプルプログラム

画像を表示

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  //画面に画像データを表示
  image(img, 0, 0);
}

位置とサイズを指定して、画像を表示

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0); //背景
  //位置と大きさを指定して、画像データを表示
  image(img, width/4, height/4, width/2, height/2);
}

画像の明度を変更 (マウスX座標で明度が変化)

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0);
  //マウスのX座標で明度を変化させる
  tint(map(mouseX, 0, width, 0, 255));
  //色を変更した画像を表示
  image(img, 0, 0);
}

画像の色を変更 (マウスの位置で色が変化)

PImage img; //画像データ

void setup() {
  size(800, 600);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0);
  //色補正をリセット
  noTint();
  //元画像を左に表示
  image(img, 0, height/4, width/2, height/2);
  //画像のRGB値をマウスのY座標で変更
  float r = mouseX/float(width) * 255;
  float g = mouseY/float(height) * 255;
  float b = 255;
  tint(r, g, b);
  //色を変更した画像を表示
  image(img, width/2, height/4, width/2, height/2);
}

マウスの位置の色を取得

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0);
  image(img, 0, 0);
  //マウスの位置のピクセルの色を取得
  color col = img.get(mouseX, mouseY);
  //取得した色で円を描画
  fill(col);
  ellipse(50, 50, 80, 80);
}

マウスの位置でモザイクの大きさが変化

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0);
  noStroke();
  //モザイクの粒度をマウスのX座標で決定
  float step = map(mouseX, 0, width, 1, 800);
  //画面の幅と高さでループ
  for (int j = 0; j < height; j += step) {
    for (int i = 0; i < width; i += step) {
      //座標を指定して色を取得
      color c = img.get(i, j);
      //取得した色を塗りの色にして四角形を描画
      fill(c);
      rect(i, j, step, step);
    }
  }
}

マウスの位置でスキャンする高さを変更

PImage img; //画像データ

void setup() {
  size(1280, 720);
  //画像を読み込み
  img = loadImage("photo.jpg");
}

void draw() {
  background(0);
  noStroke();
  //画面の幅だけループ
  for (int i = 0; i < width; i++) {
    //現在のマウスのY座標の色を取得
    color c = img.get(i, mouseY);
    //取得した色を塗りの色に
    fill(c);
    //画面の高さいっぱいに幅1pixの長方形を描画
    rect(i, 0, 1, height);
  }
}

円の大きさと色で画像を再構成する、明度がサイズに対応

PImage img; //画像データ

void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
}

void draw() {
  //色を取得する位置をランダムに決定
  int x = int(random(width));
  int y = int(random(height));
  //指定した場所の色を取得
  color col = img.get(x, y);
  fill(col, 127);
  //色の明度を円の直径にする
  float ellipseSize = map(brightness(col), 0, 255, 2, 50);
  //円を描画
  ellipse(x, y, ellipseSize, ellipseSize);
}

円の大きさと色で画像を再構成する、彩度がサイズに対応

PImage img; //画像データ

void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
}

void draw() {
  for (int i = 0; i < 10; i++) {
    //色を取得する位置をランダムに決定
    int x = int(random(width));
    int y = int(random(height));
    //指定した場所の色を取得
    color col = img.get(x, y);
    fill(col, 127);
    //色の明度を円の直径にする
    float ellipseSize = map(saturation(col), 0, 255, 2, 50);
    //円を描画
    ellipse(x, y, ellipseSize, ellipseSize);
  }
}

四角形の大きさと角度で、画像を再構成する

PImage img; //画像データ

void setup() {
  size(1280, 720);
  img = loadImage("photo.jpg");
  background(0); //背景
  noStroke(); //枠線は無し
  rectMode(CENTER); //中心を原点に四角形を描画
}

void draw() {
  for (int i=0; i<100; i++) {
    //場所をランダムに決定
    int x = int(random(width));
    int y = int(random(height));
    //色を取得
    color col = img.get(x, y);
    fill(col, 127);
    pushMatrix();
    translate(x, y);
    //色の色相を回転角度に
    rotate(map(hue(col), 0, 255, 0, PI));
    //色の再度を四角形の幅に
    float w = map(saturation(col), 0, 255, 1, 40);
    rect(0, 0, w, 1.5);
    popMatrix();
  }
}

膨張するピクセル

ArrayList bubbles; //Bubbleクラスを格納するArrayList
PImage img; //色をピックアップするイメージ
int maxSize = 60; //円の最大サイズ(直径)

void setup() {
  //画面初期設定
  size(800, 600);
  frameRate(60);
  noStroke();
  //ArrayListの初期化
  bubbles = new ArrayList();
  //画像を読み込んで、画面の大きさにリサイズ
  //画像の名前は読み込んだ画像に変更する
  img = loadImage("photo.jpg");
  img.resize(width, height);
  //最初のきっかけの円を描画
  for (int i = 0; i < 10; i++) {
    PVector loc = new PVector(random(width), random(height));
    bubbles.add(new Bubble(loc));
  }
}

void draw() {
  background(0);
  //ArrayListに格納された数だけ、Bubbleを描画
  for (int i = 0; i < bubbles.size(); i++) {
    bubbles.get(i).draw();
  }
  //Bubbleの状態を更新
  for (int i = 0; i < bubbles.size(); i++) {
    //もしアクティブな状態だったら
    if (bubbles.get(i).isDead == false) {
      //円の周囲のピクセルの色を確認
      boolean expand = bubbles.get(i).checkPixel();
      //もしこれ以上膨張できない場合
      if (expand == false) {
        //新規にBubbleを生成
        PVector loc;
        //余白がみつかるまで繰り返し
        while (true) {
          loc = new PVector(random(width), random(height));
          color c = get(int(loc.x), int(loc.y));
          if ((red(c) + blue(c) + green(c)) == 0) break;
        }
        //余白に新規Bubbleを生成
        bubbles.add(new Bubble(loc));
        bubbles.get(i).isDead = true;
      }
    }
  }
}

//マウスクリックで初期化
void mouseClicked() {
  //ArrayListをクリア
  bubbles.clear();
  //きっかけの円を描画
  for (int i = 0; i < 100; i++) {
    PVector loc = new PVector(random(width), random(height));
    bubbles.add(new Bubble(loc));
  }
}

//Bubbleクラス
//円が膨張しながら空間を充填していく
class Bubble {
  float size; //円のサイズ(直径)
  float expandSpeed; //膨張スピード
  color circleColor; //円の色
  PVector location; //中心の位置
  boolean expand; //膨張中か否か
  boolean isDead; //活動している状態か否か

  //コンスタラクター
  Bubble(PVector _location) {
    location = _location; //位置を引数から取得
    //パラメータの初期値設定
    size = 0;
    expandSpeed = 4.0;
    expand = true;
    isDead = false;
    //読み込んだ画像から中心位置と同じピクセルの色を取得
    circleColor = img.get(int(location.x), int(location.y));
  }

  //円の描画
  void draw() {
    //もし膨張中なら
    if (expand == true) {
      //指定した速度で膨張
      size += expandSpeed;
    }
    //円を描画
    fill(circleColor);
    ellipse(location.x, location.y, size, size);
  }

  //円の周囲のピクセルの色を取得して、まだ膨張する余地があるかを判断する
  boolean checkPixel() {
    //次のフレームでのサイズを計算
    float nextSize = size + expandSpeed;
    for (float i = 0; i < TWO_PI; i += 0.01) {
      //円の周囲の座標を取得
      int x = int(cos(i) * nextSize / 2.0 + location.x);
      int y = int(sin(i) * nextSize / 2.0 + location.y);
      //取得した座標の直下のピクセルの色を取得
      color c = get(x, y);
      //色が黒意外、もしくは最大サイズを超えていたら、膨張を中止
      if ((red(c) + blue(c) + green(c)) > 0 || size > maxSize) {
        expand = false;
      }
    }
    //現在の膨張の状態を返す
    return expand;
  }
}