2013-03-24 92 views
1

我使用sketch.js在HTML5畫布上動態繪製非常簡單的形狀。計算在HTML5畫布上繪製的形狀內的像素數量?

有沒有一種方法可以計算畫布上繪製的封閉形狀內的像素數量?

+0

不容易。你可以以某種方式將它分成小的梯形和近似它,但它根本不是內置的。 – 2013-03-24 20:19:19

+1

是的,我很驚訝沒有完成這項工作的文件 - 不是用於sketch.js,而是用於畫布。哦,很好 - 謝謝。 – 2013-03-24 21:31:02

+0

當你說「像素數量」時,你的意思是你想知道其邊界框的大小,或者你真的想知道不透明像素的數量? – markE 2013-03-24 21:59:45

回答

3

這裏是你如何在畫布

// 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++; } 
    } 

[編輯:獲得「如果填充」畫布上的封閉圖形的像素數]指望非透明像素

我通常使用此代碼在畫布中進行遮罩,但我已在此調整它以使您的像素數在您的封閉形狀內。

幾個caviats:

因爲「相鄰」算法被使用的,strokewidth必須至少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> 
+0

當然這不是封閉的區域,只是所有不透明的像素... – 2013-03-25 07:54:13

+2

@Rich Bradshaw--很高興認識你!那麼,如果填充形狀,它會計數填充,如果它沒有填充,它不會計數填充 - 只是中風:)但是,如果OP進一步請求包含「if-filled」的像素計數未填充的形狀 - 我也有一個解決方案。這有點複雜,但它基本上是邊緣檢測,包括識別筆劃外的所有像素,然後從畫布的總像素數中減去該數。 – markE 2013-03-25 15:21:26

+1

這聽起來很有趣 - 看演示會很酷,我經常想知道如何做邊緣檢測,但大多數例子都非常複雜! – 2013-03-25 19:59:47