差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン 次のリビジョン両方とも次のリビジョン | ||
lecture:design_with_prototyping:p5.js編:11.ピクセル再構成 [2021/02/28 14:42] – [Sample05] baba | lecture:design_with_prototyping:p5.js編:11.ピクセル再構成 [2021/03/01 16:34] – [線の本数や頂点座標のずらし方を変更してみる] baba | ||
---|---|---|---|
行 199: | 行 199: | ||
</ | </ | ||
- | |||
</ | </ | ||
</ | </ | ||
- | 次に二値化情報を元に,grayが255の箇所は色を復元することにすると,次のような結果になります. 結果として比較的明度の高いラインが残るようになりました.このように画像に対して二値化を行い, 処理を行なう対象領域を限定する手法は画像処理(Computer Vision)において非常に一般的な手法です. もちろん静止画や動画像における再構成要素としても利用価値が高いものです. | ||
- | |||
- | {{: | ||
<WRAP clear/> | <WRAP clear/> | ||
- | ===== Sample05 | + | ===== 閑話休題 |
+ | ここまで1pixelごとに真面目に処理を加えてきましたが、filter()関数を使うとこの辺のことは簡単に実装することもできます。 | ||
+ | * https:// | ||
+ | のリンクにあるように例えば次のようなコードを記述することでぼかし効果を簡単にえることもできます。 | ||
+ | {{ : | ||
<file .js sketch.js> | <file .js sketch.js> | ||
- | PImage img; | + | var sample_image; |
- | img = loadImage(" | + | |
- | size(628, 350); | + | function preload() { |
- | imageMode(CENTER); | + | |
- | noStroke(); | + | |
- | background(255); | + | |
- | for( int i = 0; i < img.height; i++ ){ | + | |
- | | + | |
- | color c = img.get(j,i); | + | |
- | float gray = (red(c)+green(c)+blue(c))/ | + | |
- | if( gray < 80 ){ | + | |
- | gray = 0; | + | |
- | } | + | |
- | else{ | + | |
- | gray = 255; | + | |
- | } | + | |
- | + | ||
- | if( gray == 255 ){ | + | |
- | stroke(red(c), | + | |
- | } | + | |
- | else{ | + | |
- | stroke(255); | + | |
- | } | + | |
- | point(j, i); | + | |
- | } | + | |
} | } | ||
- | </ | ||
- | <WRAP clear/> | ||
+ | function setup() { | ||
+ | createCanvas(500, | ||
+ | noLoop(); | ||
+ | } | ||
- | <wrap em> | + | function draw() { |
- | 上記の2値化については,手動でやるのも問題ないですが,実は画像を扱うfilter()なるものが 用意されているので,それを使えば一発でできます.ここまで真面目に1ピクセルごとに処理していましたが、これらの画像処理用の関数も使ってみましょう。たとえばBLURを利用すると、画像のボケ味を変更することができます。 | + | |
- | ====== 対象物を表現するには ====== | + | |
- | ここまでのサンプルは抽象的な画像を用いて来ましたが,少し具体的な下記画像に変えてみたいと思います. | + | |
- | {{ : | + | |
+ | image(sample_image, | ||
+ | filter(BLUR, | ||
+ | } | ||
- | 私達が見ているのは,画像のピクセルの集合体です.画面におけるピクセルとは画像を表示するための最小単位になっています. | + | </ |
- | 単位について考えてみます.単位とはなんでしょうか?なにかものを表すための基準となる目盛りのことです.ではその基準は | + | <WRAP clear/> |
- | 誰が決めるのか.それはエンジニアであったりデザイナであったりアーティストであったり,研究者であったり.単位はそれを | + | ====== 対象物を表現するには ====== |
- | 表す上で実は自由に決めて良いものでもあります.その証拠に,オーム(Ω)やニュートン(N),アンペア(A),テスラ,ボルト, | + | 私達が見ているのは,画像のピクセルの集合体です.画面におけるピクセルとは画像を表示するための最小単位になっています.単位について考えてみます.単位とはなんでしょうか?なにかものを表すための基準となる目盛りのことです.ではその基準は誰が決めるのか.それはエンジニアであったりデザイナであったりアーティストであったり,研究者であったり.単位はそれを表す上で実は自由に決めて良いものでもあります.その証拠に,オーム(Ω)やニュートン(N),アンペア(A),テスラ,ボルト,ベクレル,ヘルツなどその単位を定義付けた科学者の名前がそのまま単位になっている事例が少なからず知られています. |
- | ベクレル,ヘルツなどその単位を定義付けた科学者の名前がそのまま単位になっている事例が少なからず知られています. | + | |
- | 上記で見ている画像はニューヨークのセントラルパーク前の横断歩道前で私が撮影した画像です.800x597のピクセル数で構成 | + | 単位とは量を把握するための単なる仕組みであり、私達はこれを客観的な手法からときには超主観的な場合にもこの単位を利用して世界にアクセスしています。単位に関して個人的な短い記事を書いておいたので以下をお読みください。 |
- | されており,画素単位は1ピクセルです.ではここで,10ピクセルを1単位として考えてみます.一つの画素を一つの単位として考えるのではなく,10の画素を一つ単位として考えます. | + | * 単位について:https:// |
- | {{ : | + | さて、画素単位は1ピクセルです.ではここで,10x10ピクセルを1単位として考えてみます.一つの画素を一つの単位として考えるのではなく,10の画素を一つ単位として考えます.10ピクセルごとに丸で描画してみると,目の細かいモザイクのような効果になりました. |
- | <file .pde sample07.pde> | + | {{ : |
- | PImage img; | + | <file .js sketch.js> |
- | img = loadImage(" | + | var sample_image; |
- | size(800, 597); | + | |
- | imageMode(CENTER); | + | function preload() { |
- | noStroke(); | + | sample_image |
- | background(255); | + | } |
- | for ( int i = 0; i < img.height; i=i+10 ) { | + | |
- | for ( int j = 0; j < img.width; j=j+10 ) { | + | function setup() { |
- | | + | createCanvas(500, 336); |
- | stroke(red(c), green(c), blue(c)); | + | |
- | | + | noLoop(); |
- | | + | } |
- | point(j, i); | + | |
+ | function draw() { | ||
+ | background(0); | ||
+ | |||
+ | | ||
+ | for (let j = 0; j < sample_image.width; j+=10) { | ||
+ | | ||
+ | stroke(c); | ||
+ | strokeWeight(10); | ||
+ | point(j, i); | ||
+ | } | ||
} | } | ||
} | } | ||
</ | </ | ||
- | <WRAP center round tip 60%> | ||
- | 上記のプログラムを setup(), draw()で下記直し,tweakモードで実行してみましょう.それぞれforループにあるi, | ||
- | </ | ||
- | 10ピクセルごとに丸で描画してみると,目の細かいモザイクのような効果になりました.ではこの10ピクセル分の塊を別の単位に置き換えてみましょう. | + | ===== 文字を単位にしてみる ===== |
- | 文字' | + | |
- | {{ : | + | この10ピクセル分の塊を別の単位に置き換えてみましょう. |
- | <file .pde sample08.pde> | + | 文字' |
- | PImage img; | + | {{ : |
- | img = loadImage(" | + | <file .js sketch.js> |
- | size(800, 597); | + | var sample_image; |
- | imageMode(CENTER); | + | |
- | noStroke(); | + | function preload() { |
- | background(255); | + | sample_image |
- | for ( int i = 0; i < img.height; | + | } |
- | for ( int j = 0; j < img.width; | + | |
- | | + | function setup() { |
- | fill(red(c),green(c),blue(c)); | + | createCanvas(500, 336); |
- | text("A",j,i); | + | |
+ | noLoop(); | ||
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | background(255); | ||
+ | |||
+ | | ||
+ | for (let j = 0; j < sample_image.width; j+=10) { | ||
+ | | ||
+ | fill(c); | ||
+ | strokeWeight(0); | ||
+ | textSize(10); | ||
+ | text('A',j,i); | ||
+ | } | ||
} | } | ||
} | } | ||
</ | </ | ||
- | ====== 文字の複雑さを濃度として考え、ピクセルを描画する | + | ===== 文字の複雑さを濃度として考え、ピクセルを描画する ===== |
いわゆるアスキーアートと呼ばれる手法です。ピクセルに対応する濃度を文字種に変換することで、文字だけで絵を表現する独特の手法になります。ここまでですでに各ピクセルの明るさ情報は取得できているので、それを利用してどの文字を利用するのが良いかがわかれば実装ができそうです。[[http:// | いわゆるアスキーアートと呼ばれる手法です。ピクセルに対応する濃度を文字種に変換することで、文字だけで絵を表現する独特の手法になります。ここまでですでに各ピクセルの明るさ情報は取得できているので、それを利用してどの文字を利用するのが良いかがわかれば実装ができそうです。[[http:// | ||
char[] char_pixel = {' | char[] char_pixel = {' | ||
行 310: | 行 310: | ||
上記の配列を利用して、真っ暗だと ' | 上記の配列を利用して、真っ暗だと ' | ||
* 0-255 のピクセルbrightnessを 0 - (char_pixel.length-1)のサイズに変換する | * 0-255 のピクセルbrightnessを 0 - (char_pixel.length-1)のサイズに変換する | ||
- | * brightness(c1)とすることで、輝度情報を取得できる | + | * brightness(c)とすることで、輝度情報を取得できる |
の2つを追記できれば良いかなと思います。下記画像をよく見ると小さな文字だけで構成されているのがわかるかと思います。 | の2つを追記できれば良いかなと思います。下記画像をよく見ると小さな文字だけで構成されているのがわかるかと思います。 | ||
- | {{ : | + | {{ : |
- | <file .pde sample08_2.pde> | + | <file .js sketch.js> |
- | import processing.video.*; | + | var sample_image; |
- | char[] | + | |
+ | const char_pixel = [' | ||
' | ' | ||
' | ' | ||
- | '?', | + | '?', |
- | + | ||
- | PImage img; | + | |
- | size(800, 597); | + | |
- | img = loadImage(" | + | |
- | imageMode(CENTER); | + | |
- | noStroke(); | + | |
- | print(char_pixel.length); | + | |
- | textSize(5); | + | |
- | noFill(); | + | function preload() { |
- | background(0); | + | sample_image = loadImage(" |
- | for ( int i = 0; i < img.height; i=i+5 ) { | + | } |
- | | + | |
- | | + | function setup() { |
- | color c1 = img.get(j, i); | + | createCanvas(500, |
- | float brightness | + | |
- | text(char_pixel[int(brightness)], j, i); | + | noLoop(); |
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | | ||
+ | |||
+ | | ||
+ | for (let j = 0; j < sample_image.width; j+=2) { | ||
+ | let c = sample_image.get(j, i); | ||
+ | let pos = parseInt(map(brightness(c), | ||
+ | | ||
+ | | ||
+ | textSize(2); | ||
+ | | ||
+ | } | ||
} | } | ||
- | endShape(); | ||
} | } | ||
- | |||
</ | </ | ||
===== 線で表現する ===== | ===== 線で表現する ===== | ||
- | <WRAP center round tip 60%> | + | 上記はピクセルをベースに対象物を再構成してみましたが,次は線を用いてみます.テレビの走査線のように左から右へ向けて黒い線を引きます。ただしただ真っ直ぐな線では真っ黒なキャンバスができあがるだけですので、画素の明るさ(brightness)に応じて線を少し上方向にずらして描画してみると、以下のような出力結果を得ることができます。すこし不思議な画像になりましたね。このサンプルではパラメータを調整できるように, |
- | 以下のサンプルプログラムはsetup(), draw()の内プログラムになっています.自分でvoid setup(){} void draw(){}に書き直して | + | |
- | tweakモード実行しつつ,プログラムの振る舞いを観察してみましょう. | + | |
- | </ | + | |
- | 上記はピクセルをベースに対象物を再構成してみましたが,次は線を用いてみます. | + | {{ : |
- | {{ : | + | <WRAP group> |
- | <file .pde sample09.pde> | + | <WRAP half column> |
- | PImage img; | + | |
- | img = loadImage(" | + | <file .js sketch.js> |
- | size(800, 597); | + | var sample_image; |
- | imageMode(CENTER); | + | let x_step, y_step, y_max; |
- | noFill(); | + | |
- | background(255); | + | function preload() { |
- | for( int i = 0; i < img.height; i++ ){ | + | sample_image |
- | + | } | |
- | | + | |
- | for( int j = 0; j < img.width; j++ ){ | + | function setup() { |
- | | + | createCanvas(500, 336); |
- | float gray = (red(c)+green(c)+blue(c))/ | + | |
- | | + | |
- | vertex(j,i-gray/10.0); | + | |
+ | x_step = select('# | ||
+ | y_step = select('# | ||
+ | y_max = select('# | ||
+ | noLoop(); | ||
+ | } | ||
+ | |||
+ | function draw() { | ||
+ | | ||
+ | | ||
+ | | ||
+ | beginShape(); | ||
+ | for (let j = 0; j < sample_image.width; j += x_step) { | ||
+ | | ||
+ | let b = brightness(c); | ||
+ | b = map(b, 0, 255, 0, y_max); | ||
+ | vertex(j, i - b); | ||
+ | } | ||
+ | endShape(); | ||
} | } | ||
- | endShape(); | + | } |
+ | |||
+ | function xChanged() { | ||
+ | x_step = this.value(); | ||
+ | draw(); | ||
+ | } | ||
+ | |||
+ | function yChanged() { | ||
+ | y_step = this.value(); | ||
+ | draw(); | ||
+ | } | ||
+ | |||
+ | function maxChanged() { | ||
+ | y_max = this.value(); | ||
+ | draw(); | ||
} | } | ||
</ | </ | ||
+ | </ | ||
+ | |||
+ | <WRAP half column> | ||
+ | <file .html index.html> | ||
+ | < | ||
+ | <html lang=" | ||
+ | < | ||
+ | <script src=" | ||
+ | <script src=" | ||
+ | <link rel=" | ||
+ | <meta charset=" | ||
+ | |||
+ | </ | ||
+ | < | ||
+ | <script src=" | ||
+ | x_step:< | ||
+ | y_step:< | ||
+ | y_max:< | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | |||
ここで少し工夫している箇所は vertex 関数において,ただ座標を正しくうつのではなく,その画素の | ここで少し工夫している箇所は vertex 関数において,ただ座標を正しくうつのではなく,その画素の | ||
グレースケール値に基づいて上下に位置を動かしていることです.これにより明るめの画素は上方向に座標が | グレースケール値に基づいて上下に位置を動かしていることです.これにより明るめの画素は上方向に座標が | ||
- | 移動し,暗めの画素は下方向に画素が移動します.上記プログラムはそれを画像サイズの縦ピクセル分すべて | + | 移動し,暗めの画素は下方向に画素が移動します. |
- | 描画していますが,これを少し間引いて下記のようなプログラムに変更してみます.これまで i++ としてた | + | |
- | ものを i=i+5 と変更したのみです. | + | |
- | {{ : | + | |
- | <file .pde sample10.pde> | + | |
- | PImage img; | + | |
- | img = loadImage(" | + | |
- | 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, | + | |
- | float gray = (red(c)+green(c)+blue(c))/ | + | |
- | stroke(0, | + | |
- | vertex(j, | + | |
- | } | + | |
- | endShape(); | + | |
- | } | + | |
- | </ | + | |
+ | ==== 線の本数や頂点座標のずらし方を変更してみる ==== | ||
次はプログラムを少し最初に戻して,描く線にアルファ値をもたせてみます.アルファ値とは | 次はプログラムを少し最初に戻して,描く線にアルファ値をもたせてみます.アルファ値とは | ||
- | 透明度のことで,詳細は https://processing.org/tutorials/color/ を参照すると | + | 透明度のことで,詳細は https://p5js.org/reference/#/p5/ |
よいでしょう. | よいでしょう. | ||
この透明度を利用し,これまで真っ黒で書いていた線に対して一定の透明度をつけてみます. | この透明度を利用し,これまで真っ黒で書いていた線に対して一定の透明度をつけてみます. | ||
- | 実行結果とプログラムは下記の通りです.面白いことに結果として得られた画像は,デプスマップ(深度情報) | + | 実行結果とプログラムは下記の通りです.面白いことに結果として得られた画像は,凹凸がついた銀盤のように見えますね。 |
- | を持った画像のように見えますね. | + | |
- | {{ : | + | {{ : |
- | <file .pde sample12.pde> | + | |
- | PImage img; | + | |
- | img = loadImage(" | + | |
- | 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, | + | |
- | float gray = (red(c)+green(c)+blue(c))/ | + | |
- | stroke(0, | + | |
- | vertex(j, | + | |
- | } | + | |
- | endShape(); | + | |
- | } | + | |
- | </ | + | |