ホームページ  >  記事  >  ウェブフロントエンド  >  キャンバスを使用して画像の三角形分割 (LOW POLY) を実現するエフェクト_JavaScript スキル

キャンバスを使用して画像の三角形分割 (LOW POLY) を実現するエフェクト_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 15:14:502074ブラウズ

Ovilia が Threejs を使用して写真の平面三角形分割の効果である LOW POLY を作成しているのを偶然見たので、それが素晴らしいと思い、自分で試してみました。

あまりthreejsを使ったことがなかったので、このエフェクトはthreejsでは使えないようなので、canvasの2D描画APIを使ってやりました。

まずデモに直接アクセスします: http://whxaxes.github.io/canvas-test/src/Funny-demo/lowpoly/index.html (モバイルでも視聴できます)ただし、計算量が比較的大きいため、モバイル端末での計算はPCよりも時間がかかります)

この効果を実現するには、主に画像を三角測量し、画像に対してエッジ検出を実行する必要があります。これら 2 つのうち、1 つ目はドローネ三角形分割アルゴリズムを使用し、2 つ目はソーベル エッジ検出アルゴリズムを使用します。非常にハイエンドに聞こえますが、どちらのアルゴリズムにも、直接使用できる対応するオープンソース コンポーネントがあります。ironwallaby の delaunay コンポーネントと Miguel Mota の sobel コンポーネントです。

これら 2 つのアルゴリズムのうち、sobel の方が優れていますが、delaunay は少し複雑です。今後研究していきます。ただし、現時点ではエフェクトを作成するだけの場合は、これらのコンポーネントを引き続き使用できます。

2 つの最も重要なコンポーネントが配置されたので、残りは非常に簡単です:

まず、キャンバスに画像を描画します:

canvas.width = (img.width > 800) ? 800 : img.width;
canvas.height = img.height * canvas.width/img.width;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); 

その後、canvas の imgData を取得し、sobel 計算を通じて新しい imgData を返します

var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var newImgData = Sobel(imgData);

newImgData をキャンバスに配置すると、次のようにカラー画像がグレースケール画像になることがわかります:

上記の Sobel コンポーネントは私の用途にはあまり適しておらず、コードも不適切だったので、適切な修正と最適化を行い、ループメソッドを最適化し、計算速度を高速化し、コールバック関数を追加しました。詳細については、プロジェクト github
の sobel.js ファイルを参照してください。

Sobel メソッドで imgData.data を走査すると、コールバック関数が呼び出され、40 を超えるカラー値 (つまり、グレースケールが rgb(40,40,40) を超える) の座標点が次のようになります。コールバックに記録されます。次にエッジ点の一部をランダムに取得し、ランダムな座標と四隅の座標値を加算します。このようにして、必要な座標点を取得できます

var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 收集色值大于40的边缘像素点
var collectors = [];
Sobel(imgData , function(value , x , y){
if(value > 40){collectors.push([x , y]);}
});
// 添加一些随机点
for(var i=0;i<300;i++){particles.push([Math.random()*canvas.width , Math.random()*canvas.height]);}
// 添加随机边缘点,数量为边缘点数量除于50
var length = ~~(collectors.length/50), random;
for(var l=0;l<length;l++){
random = (Math.random()*collectors.length)<<0;
particles.push(collectors[random]);
collectors.splice(random , 1);
}
// 添加四顶点坐标
particles.push([0,0] , [0,canvas.height] , [canvas.width,0] , [canvas.width,canvas.height]);

座標点を取得した後、ドロネー コンポーネントを使用して三角座標配列を正しい順序で計算して取得できます。これらの配列内の点を接続すると、次のような効果が得られます。

もちろん、私たちが望む効果は線を接続することではなく、すべての三角形を色で塗りつぶすことです。つまり、三角形の 3 つの座標を取得し、中心点の座標を計算して、対応する座標を取得します。中心点の座標の RGB カラー値に基づいて imgData の座標を取得し、それを三角形の領域に塗りつぶします。

// 使用delaunay三角化获取三角坐标
var triangles = Delaunay.triangulate(particles);
var x1,x2,x3,y1,y2,y3,cx,cy;
for(var i=0;i < triangles.length; i+=3) {
x1 = particles[triangles[i]][0];
x2 = particles[triangles[i+1]][0];
x3 = particles[triangles[i+2]][0];
y1 = particles[triangles[i]][1];
y2 = particles[triangles[i+1]][1];
y3 = particles[triangles[i+2]][1];
// 获取三角形中心点坐标
cx = ~~((x1 + x2 + x3) / 3);
cy = ~~((y1 + y2 + y3) / 3);
// 获取中心点坐标的颜色值
index = (cy*imgData.width + cx)*4;
var color_r = imgData.data[index];
var color_g = imgData.data[index+1];
var color_b = imgData.data[index+2];
// 绘制三角形
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fillStyle = "rgba("+color_r+","+color_g+","+color_b+",1)";
ctx.fill();
ctx.restore();
}
上記で注意すべき点は、正しいカラー パラメーターを取得するには、取得した中心点の座標を丸める必要があるということです。丸めずに、RGB インデックスを取得するときに丸めたい場合は、色の値が間違っています。なぜなら、この方法で得られたピクセルは、必要な中心ピクセルではないからです。


色を取得した後、単純な接続と塗りつぶし操作を行うと、最終的な効果は次のようになります。

上記のコンテンツでは、キャンバスを使用して画像 (LOW POLY) を三角形分割する効果を実現する方法を紹介しています。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。