ホームページ > 記事 > ウェブフロントエンド > キャンバスを使用して画像の三角形分割 (LOW POLY) を実現するエフェクト_JavaScript スキル
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(); }
上記のコンテンツでは、キャンバスを使用して画像 (LOW POLY) を三角形分割する効果を実現する方法を紹介しています。