ピクセルの再構成

まずは下記静止画像を読み込み,それを表示するだけのプログラムを記述してみます.静止画像を別名保存し, スケッチファイルのdataファイルの中にtest.pngとして保存してください. Processing3以上の場合,size()関数に変数を引き渡すことが禁止されてしまっているので,直接画像サイズを入力してください.今回のtest.pngの画像サイズは628,350となっています.

sample1.pde
// Processing v.3
PImage img;
img = loadImage("test.png");
size(628, 350);
image(img,0,0,img.width, img.height);

たった4行ですが,指定した画像を表示するプログラムが書けました.ではこの画像を再構成する にはどのようなことをすればよいでしょうか?デジタル画像において,全ての画像はピクセルから 構成されます.つまりピクセルデータを扱うことで,同じ画像データから様々な表示に再構成できる はずです.上記プログラムではimage関数から直接PImageの中身を描画していますが,これでは 各ピクセル画像の値を操作することができていません.そこで,次のようなプログラムに変えること で,各ピクセルデータを取得し,点描画と同じやり方で静止画像を描画してみます.

sample2.pde
PImage img;
img = loadImage("test.png");
size(628, 350);
imageMode(CENTER);
noStroke();
background(255);
for( int i = 0; i < img.height; i++ ){
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    stroke(red(c), green(c), blue(c));
    point(j, i);  
  }
}

実行結果はsample1と変わらないことが確認できたと思います.では再構成の簡単な事例として, 各色データにおけるRGBをBGRの順に入れ替えて表示してみましょう.

sample3.pde
PImage img;
img = loadImage("test.png");
size(628, 350);
imageMode(CENTER);
noStroke();
background(255);
for( int i = 0; i < img.height; i++ ){
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    stroke(blue(c), green(c), red(c));
    point(j, i);  
  }
}

実行してみて,一見するとなにも変わっていないように感じるかもしれませんが, 赤色と青色の部分が元画像から入れ替わっているのがわかると思います.では次にグレースケールに変更 してみましょう.RGBに各色を設定していましたが,ここで,RGB画素の平均値を与えることで, グレースケールに変更出来ます.

sample4.pde
PImage img;
img = loadImage("test.png");
size(628, 350);
imageMode(CENTER);
noStroke();
background(255);
for( int i = 0; i < img.height; i++ ){
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    stroke(gray,gray,gray);
    point(j, i);  
  }
}

ここで,イメージをグレースケールにすることができました.では次に,閾値(しきいち)を設けて, 画像を二値化してみましょう.つまりこの画像の明暗を分ける処理になります.すでにグレースケールの 値は各画素において取得できているので,これと適当な値をくれべることで,画像を白と黒の二値に してみます.

sample5.pde
PImage img;
img = loadImage("test.png");
size(628, 350);
imageMode(CENTER);
noStroke();
background(255);
for( int i = 0; i < img.height; i++ ){
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    if( gray < 50 ){
      gray = 0;
    }
    else{
      gray = 255;
    }
    stroke(gray,gray,gray);
    point(j, i);  
  }
}

次に二値化情報を元に,grayが255の箇所は色を復元することにすると,次のような結果になります. 結果として比較的明度の高いラインが残るようになりました.このように画像に対して二値化を行い, 処理を行なう対象領域を限定する手法は画像処理(Computer Vision)において非常に一般的な手法です. もちろん静止画や動画像における再構成要素としても利用価値が高いものです.

sample6.pde
PImage img;
img = loadImage("test.png");
size(628, 350);
imageMode(CENTER);
noStroke();
background(255);
for( int i = 0; i < img.height; i++ ){
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    if( gray < 80 ){
      gray = 0;
    }
    else{
      gray = 255;
    }
 
    if( gray == 255 ){
      stroke(red(c),green(c),blue(c));
    }
    else{
      stroke(255);
    }
    point(j, i);  
  }
}

練習 上記の2値化については,手動でやるのも問題ないですが,実はPImageのメソッドにはfilter()なるものが 用意されているので,それを使えば一発でできます. http://processing.org/reference/PImage_filter_.html を参考にして,同じ事をfilter関数を利用して実装してみましょう.

対象物を表現するには

ここまでのサンプルは抽象的な画像を用いて来ましたが,少し具体的な下記画像に変えてみたいと思います.  サンプルとなる画像

私達が見ているのは,画像のピクセルの集合体です.画面におけるピクセルとは画像を表示するための最小単位になっています. 単位について考えてみます.単位とはなんでしょうか?なにかものを表すための基準となる目盛りのことです.ではその基準は 誰が決めるのか.それはエンジニアであったりデザイナであったりアーティストであったり,研究者であったり.単位はそれを 表す上で実は自由に決めて良いものでもあります.その証拠に,オーム(Ω)やニュートン(N),アンペア(A),テスラ,ボルト, ベクレル,ヘルツなどその単位を定義付けた科学者の名前がそのまま単位になっている事例が少なからず知られています.

上記で見ている画像はニューヨークのセントラルパーク前の横断歩道前で私が撮影した画像です.800×597のピクセル数で構成 されており,画素単位は1ピクセルです.ではここで,100ピクセルを1単位として考えてみます.一つの画素を一つの単位として考えるのではなく,100の画素を一つ単位として考えます.

sample07.pde
PImage img;
img = loadImage("sample.jpg");
size(800, 597);
imageMode(CENTER);
noStroke();
background(255);
for ( int i = 0; i < img.height; i=i+10 ) {
  for ( int j = 0; j < img.width; j=j+10 ) {
    color c = img.get(j, i);
    stroke(red(c), green(c), blue(c));
    fill(red(c),green(c),blue(c));    
    strokeWeight(10);
    point(j, i);
  }
}

10ピクセルごとに丸で描画してみると,目の細かいモザイクのような効果になりました.次は文字'A'を単位にしてみます.

sample08.pde
PImage img;
img = loadImage("sample.jpg");
size(800, 597);
imageMode(CENTER);
noStroke();
background(255);
for ( int i = 0; i < img.height; i=i+10 ) {
  for ( int j = 0; j < img.width; j=j+10 ) {
    color c = img.get(j, i);
    fill(red(c),green(c),blue(c));    
    text("A",j,i);
  }
}

上記はピクセルをベースに対象物を再構成してみましたが,次は線を用いてみます.

sample09.pde
PImage img;
img = loadImage("sample.jpg");
size(800, 597);
imageMode(CENTER);
noFill();
background(255);
for( int i = 0; i < img.height; i++ ){
 
  beginShape();
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    stroke(0,0,0);
    vertex(j,i-gray/10.0);
  }
  endShape();
}

ここで少し工夫している箇所は vertex 関数において,ただ座標を正しくうつのではなく,その画素の グレースケール値に基づいて上下に位置を動かしていることです.これにより明るめの画素は上方向に座標が 移動し,暗めの画素は下方向に画素が移動します.上記プログラムはそれを画像サイズの縦ピクセル分すべて 描画していますが,これを少し間引いて下記のようなプログラムに変更してみます.これまで i++ としてた ものを i=i+5 と変更したのみです.

sample10.pde
PImage img;
img = loadImage("sample.jpg");
size(800, 597);
imageMode(CENTER);
noFill();
background(255);
for( int i = 0; i < img.height; i=i+5 ){ // 変更箇所
 
  beginShape();
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    stroke(0,0,0);
    vertex(j,i-gray/10.0);
  }
  endShape();
}

次はプログラムを少し最初に戻して,描く線にアルファ値をもたせてみます.アルファ値とは 透明度のことで,詳細は https://processing.org/tutorials/color/ を参照すると よいでしょう.

この透明度を利用し,これまで真っ黒で書いていた線に対して一定の透明度をつけてみます. 実行結果とプログラムは下記の通りです.面白いことに結果として得られた画像は,デプスマップ(深度情報) を持った画像のように見えますね.

sample12.pde
PImage img;
img = loadImage("sample.jpg");
size(800, 597);
imageMode(CENTER);
noFill();
background(255);
for( int i = 0; i < img.height; i++ ){
 
  beginShape();
  for( int j = 0; j < img.width; j++ ){
    color c = img.get(j,i);
    float gray = (red(c)+green(c)+blue(c))/3;
    stroke(0,0,0,100);
    vertex(j,i-gray/10.0);
  }
  endShape();
}
  • lecture/design_with_prototyping/02.ピクセル再構成.txt
  • 最終更新: 2018/10/24 09:15
  • by baba