计算 HTML5 画布上绘制的形状内的像素数?

Eva*_*olo 1 html jquery html5-canvas

我正在使用sketch.js在 HTML5 画布上动态绘制非常简单的形状。

有没有办法计算画布上绘制的闭合形状内的像素数?

mar*_*rkE 5

以下是计算画布上不透明像素的方法

\n\n
// get a reference to your canvas\nvar c=document.getElementById("canvas");\nvar ctx=c.getContext("2d");\n\n// get the pixel data from the canvas\nvar imgData=ctx.getImageData(0,0,c.width,c.height);\n\n// loop through each pixel and count non-transparent pixels\nvar count=0;\nfor (var i=0;i<imgData.data.length;i+=4)\n  {\n      // nontransparent = imgData.data[i+3]==0\n      if(imgData.data[i+3]==0){ count++; }\n  }\n
Run Code Online (Sandbox Code Playgroud)\n\n

[编辑:获取画布上“如果填充”闭合形状的像素计数]

\n\n

我通常使用此代码在画布中进行遮罩,但我\xe2\x80\x99已在此处对其进行了调整,以获取闭合形状内的像素计数。

\n\n

一些注意事项:

\n\n

由于使用 \xe2\x80\x9cneighboring\xe2\x80\x9d 算法,因此对于非弯曲形状,笔画宽度必须至少为 2 像素宽;对于包含曲线的形状,笔划宽度必须至少为 3 像素宽。

\n\n

由于 Canvas 自动使用抗锯齿功能绘制笔画,因此内部像素数始终会比预期稍大。这是因为抗锯齿 \xe2\x80\x9ceats 到 \xe2\x80\x9d 笔画中,有效地导致比预期更多的内部像素。顺便说一句,没有办法关闭canvas\xe2\x80\x99s抗锯齿,如果你尝试getImageData(),将所有形状像素设置为rbg(0,0,0),putImageData(),生成的图像也会抗锯齿\xe2\x80\x94只是更加锯齿!

\n\n

这是代码:

\n\n
<!DOCTYPE html>\n<html>\n  <head>\n<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>\n<style>\n    canvas{border:1px solid red;}\n</style>\n\n<script>\n$(function(){\n\n//  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.\n\n    var canvas=document.getElementById("canvas");\n    var context = canvas.getContext("2d");\n    var canvasWidth = canvas.width;\n    var canvasHeight = canvas.height;\n    var strokeColor =  {r: 0, g: 0, b: 0};\n    var fillColor =  {r: 101,g: 155,b: 65};\n    var fillData;\n    var strokeData;\n\n    function redraw() {\n        context.clearRect(0, 0, canvasWidth, canvasHeight);\n        context.putImageData(fillData, 0, 0);\n        drawOutline(context);\n    }\n\n    function matchstrokeColor(r, g, b, a) {\n      // must check for near black because of anti-aliasing\n      return (r + g + b < 100 && a === 255);  \n    }\n\n    function matchStartColor(pixelPos, startR, startG, startB) {\n\n      var r = strokeData.data[pixelPos],\n        g = strokeData.data[pixelPos + 1],\n        b = strokeData.data[pixelPos + 2],\n        a = strokeData.data[pixelPos + 3];\n\n      // If current pixel of the outline image is black-ish\n      if (matchstrokeColor(r, g, b, a)) {\n        return false;\n      }\n\n      r = fillData.data[pixelPos];\n      g = fillData.data[pixelPos + 1];\n      b = fillData.data[pixelPos + 2];\n\n      // If the current pixel matches the clicked color\n      if (r === startR && g === startG && b === startB) {\n        return true;\n      }\n\n      // If current pixel matches the new color\n      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {\n        return false;\n      }\n\n      return true;\n    }\n\n    function setPixel(pixelPos, r, g, b, a) {\n      fillData.data[pixelPos] = r;\n      fillData.data[pixelPos + 1] = g;\n      fillData.data[pixelPos + 2] = b;\n      fillData.data[pixelPos + 3] = a !== undefined ? a : 255;\n    }\n\n    function floodFill(startX, startY, startR, startG, startB) {\n\n      var newPos;\n      var x;\n      var y;\n      var   pixelPos;\n      var   neighborLeft;\n      var   neighborRight;\n      var   pixelStack = [[startX, startY]];\n\n      while (pixelStack.length) {\n\n        newPos = pixelStack.pop();\n        x = newPos[0];\n        y = newPos[1];\n\n        // Get current pixel position\n        pixelPos = (y * canvasWidth + x) * 4;\n\n        // Go up as long as the color matches and are inside the canvas\n        while (y >= 0 && matchStartColor(pixelPos, startR, startG, startB)) {\n          y -= 1;\n          pixelPos -= canvasWidth * 4;\n        }\n\n        pixelPos += canvasWidth * 4;\n        y += 1;\n        neighborLeft = false;\n        neighborRight = false;\n\n        // Go down as long as the color matches and in inside the canvas\n        while (y <= (canvasHeight-1) && matchStartColor(pixelPos, startR, startG, startB)) {\n          y += 1;\n\n          setPixel(pixelPos, fillColor.r, fillColor.g, fillColor.b);\n\n          if (x > 0) {\n            if (matchStartColor(pixelPos - 4, startR, startG, startB)) {\n              if (!neighborLeft) {\n                // Add pixel to stack\n                pixelStack.push([x - 1, y]);\n                neighborLeft = true;\n              }\n            } else if (neighborLeft) {\n              neighborLeft = false;\n            }\n          }\n\n          if (x < (canvasWidth-1)) {\n            if (matchStartColor(pixelPos + 4, startR, startG, startB)) {\n              if (!neighborRight) {\n                // Add pixel to stack\n                pixelStack.push([x + 1, y]);\n                neighborRight = true;\n              }\n            } else if (neighborRight) {\n              neighborRight = false;\n            }\n          }\n\n          pixelPos += canvasWidth * 4;\n        }\n      }\n    }\n\n    // Fill\n    function paintAt(startX, startY) {\n\n      var pixelPos = (startY * canvasWidth + startX) * 4,\n        r = fillData.data[pixelPos],\n        g = fillData.data[pixelPos + 1],\n        b = fillData.data[pixelPos + 2],\n        a = fillData.data[pixelPos + 3];\n\n      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {\n        // this one\'s already filled\n        return;\n      }\n\n      if (matchstrokeColor(r, g, b, a)) {\n        return;\n      }\n\n      floodFill(startX, startY, r, g, b);\n\n      redraw();\n    }\n\n\n    function init() {\n\n      var theShapes=document.getElementById("theShapes");\n      var theShapesContext=theShapes.getContext("2d");\n      drawOutline(theShapesContext);\n\n      drawOutline(context);\n\n      strokeData = context.getImageData(0, 0, canvasWidth, canvasHeight);\n      context.clearRect(0, 0, context.canvas.width, context.canvas.height);\n      fillData = context.getImageData(0, 0, canvasWidth, canvasHeight);\n\n      $(\'#canvas\').mousedown(function (e) {\n        // Mouse down location\n        var mouseX = e.pageX - this.offsetLeft;\n        var mouseY = e.pageY - this.offsetTop;\n        paintAt(mouseX, mouseY);\n      });\n\n      redraw();\n    };\n\n    function drawOutline(theContext){   \n        theContext.beginPath();\n        theContext.moveTo(55, 60);\n        theContext.bezierCurveTo(35,  70,  35, 95,  85, 95);\n        theContext.bezierCurveTo( 95,110, 130,110, 140, 95);\n        theContext.bezierCurveTo(180, 95, 180, 80, 165, 70);\n        theContext.bezierCurveTo(185, 40, 155, 35, 140, 45);\n        theContext.bezierCurveTo(130, 25,  95, 30,  95, 45);\n        theContext.bezierCurveTo( 70, 25,  45, 30,  55, 60);\n        theContext.closePath();\n        theContext.rect(200,30,100,70);\n        theContext.lineWidth = 3;\n        theContext.strokeStyle = \'rgb(0,0,0)\';\n        theContext.stroke();        \n    }\n\n    function getFilledPixelCount(theContext,theCanvas){\n        // get the pixel data from the fill canvas\n        var imgData=theContext.getImageData(0,0,theCanvas.width,theCanvas.height);\n        console.log(imgData.data.length);\n        var count=0;\n        for (var i=0;i<imgData.data.length;i+=4){\n              r = imgData.data[i],\n              g = imgData.data[i + 1],\n              b = imgData.data[i + 2],\n              a = imgData.data[i + 3];\n              if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {\n                  count++;\n              }\n          }\n        return(count);\n    }\n\n    $("#counter").click(function(){alert("There are "+getFilledPixelCount(context,canvas)+" filled pixels.");});\n\n    init();\n\n}); // end $(function(){});\n\n</script>\n\n  </head>\n  <body>\n        <p>The original stroked shapes</p>\n        <canvas id="theShapes" width=350 height=150></canvas><br/>\n        <p>The filled shapes used for pixel counting</p>\n        <p>Click inside a shape below</p>\n        <canvas id="canvas" width=350 height=150></canvas><br/>\n        <button id="counter">Filled Count</button>\n  </body>\n</html>\n
Run Code Online (Sandbox Code Playgroud)\n

  • @Rich Bradshaw——很高兴认识你!好吧,如果形状被填充,它会计算填充,如果没有填充,它不会计算填充——只是笔画:)但是,如果OP进一步请求一个包含“if-filled”的像素计数未填充的形状——我也有一个解决方案。它有点复杂,但它基本上是边缘检测,涉及识别笔划之外的所有像素,然后从画布的总像素计数中减去该计数。 (2认同)