1

私はsketch.jsを使用して、HTML5キャンバス上に非常に単純な形状を動的に描画しています。

キャンバスに描かれた閉じた形状の内側のピクセル数を計算する方法はありますか?

4

1 に答える 1

4

キャンバス上の不透明なピクセルをカウントする方法は次のとおりです

// get a reference to your canvas
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");

// get the pixel data from the canvas
var imgData=ctx.getImageData(0,0,c.width,c.height);

// loop through each pixel and count non-transparent pixels
var count=0;
for (var i=0;i<imgData.data.length;i+=4)
  {
      // nontransparent = imgData.data[i+3]==0
      if(imgData.data[i+3]==0){ count++; }
  }

[編集:キャンバス上の「塗りつぶされた」閉じた形状のピクセル数を取得する]

私は通常、このコードを使用してキャンバスでマスキングを行いますが、ここでは、閉じた形状内のピクセル数を取得するためにこのコードを調整しました。

いくつかのキャビア:

「隣接」アルゴリズムが使用されているため、ストローク幅は、曲線のない形状の場合は少なくとも2ピクセル幅、曲線を含む形状の場合は少なくとも3ピクセル幅である必要があります。

Canvasはアンチエイリアシングを使用してストロークを自動的に描画するため、内側のピクセル数は常に予想よりもわずかに大きくなります。これは、アンチエイリアシングがストロークに「食い込み」、予想よりも多くの内側のピクセルを効果的に引き起こすためです。ところで、キャンバスのアンチエイリアスをオフにする方法はありません。getImageData()を試して、すべての形状ピクセルをrbg(0,0,0)、putImageData()に設定すると、結果の画像もアンチエイリアスされます。もっとギザギザ!

コードは次のとおりです。

<!DOCTYPE html>
<html>
  <head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

//  The floodFill algorithm below is based on the good work by William Malone, Copyright 2010 William Malone (www.williammalone.com) -- Apache License: http://www.apache.org/licenses/LICENSE-2.0 -- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

    var canvas=document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var canvasWidth = canvas.width;
    var canvasHeight = canvas.height;
    var strokeColor =  {r: 0, g: 0, b: 0};
    var fillColor =  {r: 101,g: 155,b: 65};
    var fillData;
    var strokeData;

    function redraw() {
        context.clearRect(0, 0, canvasWidth, canvasHeight);
        context.putImageData(fillData, 0, 0);
        drawOutline(context);
    }

    function matchstrokeColor(r, g, b, a) {
      // must check for near black because of anti-aliasing
      return (r + g + b < 100 && a === 255);  
    }

    function matchStartColor(pixelPos, startR, startG, startB) {

      var r = strokeData.data[pixelPos],
        g = strokeData.data[pixelPos + 1],
        b = strokeData.data[pixelPos + 2],
        a = strokeData.data[pixelPos + 3];

      // If current pixel of the outline image is black-ish
      if (matchstrokeColor(r, g, b, a)) {
        return false;
      }

      r = fillData.data[pixelPos];
      g = fillData.data[pixelPos + 1];
      b = fillData.data[pixelPos + 2];

      // If the current pixel matches the clicked color
      if (r === startR && g === startG && b === startB) {
        return true;
      }

      // If current pixel matches the new color
      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return false;
      }

      return true;
    }

    function setPixel(pixelPos, r, g, b, a) {
      fillData.data[pixelPos] = r;
      fillData.data[pixelPos + 1] = g;
      fillData.data[pixelPos + 2] = b;
      fillData.data[pixelPos + 3] = a !== undefined ? a : 255;
    }

    function floodFill(startX, startY, startR, startG, startB) {

      var newPos;
      var x;
      var y;
      var   pixelPos;
      var   neighborLeft;
      var   neighborRight;
      var   pixelStack = [[startX, startY]];

      while (pixelStack.length) {

        newPos = pixelStack.pop();
        x = newPos[0];
        y = newPos[1];

        // Get current pixel position
        pixelPos = (y * canvasWidth + x) * 4;

        // Go up as long as the color matches and are inside the canvas
        while (y >= 0 && matchStartColor(pixelPos, startR, startG, startB)) {
          y -= 1;
          pixelPos -= canvasWidth * 4;
        }

        pixelPos += canvasWidth * 4;
        y += 1;
        neighborLeft = false;
        neighborRight = false;

        // Go down as long as the color matches and in inside the canvas
        while (y <= (canvasHeight-1) && matchStartColor(pixelPos, startR, startG, startB)) {
          y += 1;

          setPixel(pixelPos, fillColor.r, fillColor.g, fillColor.b);

          if (x > 0) {
            if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
              if (!neighborLeft) {
                // Add pixel to stack
                pixelStack.push([x - 1, y]);
                neighborLeft = true;
              }
            } else if (neighborLeft) {
              neighborLeft = false;
            }
          }

          if (x < (canvasWidth-1)) {
            if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
              if (!neighborRight) {
                // Add pixel to stack
                pixelStack.push([x + 1, y]);
                neighborRight = true;
              }
            } else if (neighborRight) {
              neighborRight = false;
            }
          }

          pixelPos += canvasWidth * 4;
        }
      }
    }

    // Fill
    function paintAt(startX, startY) {

      var pixelPos = (startY * canvasWidth + startX) * 4,
        r = fillData.data[pixelPos],
        g = fillData.data[pixelPos + 1],
        b = fillData.data[pixelPos + 2],
        a = fillData.data[pixelPos + 3];

      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        // this one's already filled
        return;
      }

      if (matchstrokeColor(r, g, b, a)) {
        return;
      }

      floodFill(startX, startY, r, g, b);

      redraw();
    }


    function init() {

      var theShapes=document.getElementById("theShapes");
      var theShapesContext=theShapes.getContext("2d");
      drawOutline(theShapesContext);

      drawOutline(context);

      strokeData = context.getImageData(0, 0, canvasWidth, canvasHeight);
      context.clearRect(0, 0, context.canvas.width, context.canvas.height);
      fillData = context.getImageData(0, 0, canvasWidth, canvasHeight);

      $('#canvas').mousedown(function (e) {
        // Mouse down location
        var mouseX = e.pageX - this.offsetLeft;
        var mouseY = e.pageY - this.offsetTop;
        paintAt(mouseX, mouseY);
      });

      redraw();
    };

    function drawOutline(theContext){   
        theContext.beginPath();
        theContext.moveTo(55, 60);
        theContext.bezierCurveTo(35,  70,  35, 95,  85, 95);
        theContext.bezierCurveTo( 95,110, 130,110, 140, 95);
        theContext.bezierCurveTo(180, 95, 180, 80, 165, 70);
        theContext.bezierCurveTo(185, 40, 155, 35, 140, 45);
        theContext.bezierCurveTo(130, 25,  95, 30,  95, 45);
        theContext.bezierCurveTo( 70, 25,  45, 30,  55, 60);
        theContext.closePath();
        theContext.rect(200,30,100,70);
        theContext.lineWidth = 3;
        theContext.strokeStyle = 'rgb(0,0,0)';
        theContext.stroke();        
    }

    function getFilledPixelCount(theContext,theCanvas){
        // get the pixel data from the fill canvas
        var imgData=theContext.getImageData(0,0,theCanvas.width,theCanvas.height);
        console.log(imgData.data.length);
        var count=0;
        for (var i=0;i<imgData.data.length;i+=4){
              r = imgData.data[i],
              g = imgData.data[i + 1],
              b = imgData.data[i + 2],
              a = imgData.data[i + 3];
              if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
                  count++;
              }
          }
        return(count);
    }

    $("#counter").click(function(){alert("There are "+getFilledPixelCount(context,canvas)+" filled pixels.");});

    init();

}); // end $(function(){});

</script>

  </head>
  <body>
        <p>The original stroked shapes</p>
        <canvas id="theShapes" width=350 height=150></canvas><br/>
        <p>The filled shapes used for pixel counting</p>
        <p>Click inside a shape below</p>
        <canvas id="canvas" width=350 height=150></canvas><br/>
        <button id="counter">Filled Count</button>
  </body>
</html>
于 2013-03-24T22:36:28.030 に答える