D3.jsを使ってジェネレーティブアートを作成してみた
最近、D3.jsを使ってジェネレーティブアートの勉強をしているので、ゴラン・レヴィンの作品を参考にボロノイ図エフェクトを画像に適用してみました。
Segmentation and Symptom – Interactive Art by Golan Levin and Collaborators
画像をボロノイ図にする
選んだ画像に他意はありません。元ネタがボロノイ図を使うことで被写体の「今にも崩れ落ちてしまいそうな繊細さ」を表現していたので、逆を狙ってみました。
サンプルコード
概要を簡単に説明すると、imgタグで読み込んだ画像をcanvasに転写し、グレースケール化したのちにエッジを抽出、ピクセルの濃淡に閾値を設定し超えたピクセルの座標をD3.jsのvoronoi関数に渡して描画しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
onload = function() { draw(); }; function draw() { var width = 705, height = 375; //画像処理用のキャンバス var canvas = document.getElementById('processing'); if ( ! canvas || ! canvas.getContext ) { return false; } var ctx = canvas.getContext('2d'); var img = document.querySelector("#input"); ctx.drawImage(img, 0, 0); //画像ファイルをcanvasに転写する var imgd = ctx.getImageData(0, 0, width, height); //canvasからピクセルのデータ(配列)を取得する var data = imgd.data; var gray = new Uint8ClampedArray(data.length); var edge = new Uint8ClampedArray(data.length); toGray(data, gray); //グレースケール化 var position = getEdge(gray, width, height, 2) //エッジを抽出し特徴点を返す ctx.putImageData(imgd, 0, 0); //元画像をcanvasに出力する d3main(position) } //d3でボロノイ図を書く function d3main(data) { var svg = d3.select("svg") var voronoi = d3.geom.voronoi() .x(function(d){ return d.x } ) .y(function(d){ return d.y } ); var vor = voronoi(data); var cell = svg .selectAll("g") .data(data, function(d,i){ return i }) .enter() .append("g") .append("path") .transition() .attr({ "d":function(d, i) { return "M" + vor[i].join("L") + "Z"}, stroke:"black", fill:"none", }) } //エッジ抽出 function getEdge(gray, width, height, step) { var tmp = [] for (var y = 0; y < height - 1; y+=step) { for (var x = 0; x < width - 1; x+=step) { var i = x + y * width; var r_i = (x + 1) + y * width; var ex = gray[r_i] - gray[i]; var d_i = x + (y + 1) * width; var ey = gray[d_i] - gray[i]; var ez = Math.sqrt(ex * ex + ey * ey); if (ez * 2 > 10) tmp.push({x:x, y:y}) } } return tmp } //グレイスケール化 function toGray(rgba, gray) { var length = rgba.length; var total = 0; for (var i = 0; i < length; i += 4) { var g = 0.30 * rgba[i + 0] + 0.59 * rgba[i + 1] + 0.11 * rgba[i + 2]; gray[i/4] = g; total += g; } return total / (length / 4); } |
その他
・ canvasに出力
アウトプット先をsvgではなくcanvasにしてみました。
同じ出力結果になってもつまらないので、ドロップシャドウエフェクトなんぞを掛けています。
svgだとこのようなエフェクトを掛けると、要素の数によっては非常に重くなるのですが、canvasだと快適ですね。イントラクションを必要としないのであれば、canvasは便利です。
データのインプットをimg要素ではなくvideo要素に変えて動的に変化するボロノイ図を描画しています。ちょっとだけフラクタル。