簡體   English   中英

如何檢測透明畫布上的形狀?

[英]How to detect shape on a transparent canvas?

我正在尋找一種在透明 PNG 中檢測形狀的方法。 例如,我將創建一個 940x680 的透明畫布,然后在該畫布的某處放置一個完全不透明的對象。

我希望能夠檢測該對象的大小 (w, h) 和頂部 + 左側位置。

這是原始圖像的示例:

帶有圖像對象的透明 PNG 畫布

這是我想要實現的示例(邊界框疊加,帶有頂部 + 左邊距數據):

結果圖片

我找到了一個可以進行一些透明度檢測的資源,但我不確定如何將這樣的東西擴展到我正在尋找的東西。

var imgData,
    width = 200,
    height = 200;

$('#mask').bind('mousemove', function(ev){
    if(!imgData){ initCanvas(); }
    var imgPos = $(this).offset(),
      mousePos = {x : ev.pageX - imgPos.left, y : ev.pageY - imgPos.top},
      pixelPos = 4*(mousePos.x + height*mousePos.y),
         alpha = imgData.data[pixelPos+3];

    $('#opacity').text('Opacity = ' + ((100*alpha/255) << 0) + '%');
});

function initCanvas(){
    var canvas = $('<canvas width="'+width+'" height="'+height+'" />')[0],
           ctx = canvas.getContext('2d');

    ctx.drawImage($('#mask')[0], 0, 0);
    imgData = ctx.getImageData(0, 0, width, height);
}

小提琴

你需要做什么:

  • 獲取緩沖區
  • 獲取該緩沖區的 32 位引用(如果您的其他像素是透​​明的,那么您可以使用 Uint32Array 緩沖區進行迭代)。
  • 掃描 0 - 寬度以找到 x1 邊
  • 掃描寬度 - 0 以找到 x2 邊
  • 掃描 0 - 高度以找到 y1 邊
  • 掃描高度 - 0 以找到 y2 邊緣

這些掃描可以合並,但為簡單起見,我將分別顯示每個步驟。

可以在這里找到在線演示。

結果:

快照

加載圖像時將其繪制(如果圖像很小,那么此示例的其余部分將是浪費,因為您在繪制時會知道坐標 - 假設您繪制的圖像很大,其中有一個小圖像)

(注意:為簡單起見,這是一個未優化的版本)

ctx.drawImage(this, 0, 0, w, h);

var idata = ctx.getImageData(0, 0, w, h),      // get image data for canvas
    buffer = idata.data,                       // get buffer (unnes. step)
    buffer32 = new Uint32Array(buffer.buffer), // get a 32-bit representation
    x, y,                                      // iterators
    x1 = w, y1 = h, x2 = 0, y2 = 0;            // min/max values

然后掃描每個邊緣。 對於左邊緣,您從 0 到每條線的寬度掃描(未優化):

// get left edge
for(y = 0; y < h; y++) {                       // line by line
    for(x = 0; x < w; x++) {                   // 0 to width
        if (buffer32[x + y * w] > 0) {         // non-transparent pixel?
            if (x < x1) x1 = x;                // if less than current min update
        }
    }
}

對於右邊緣,您只需反轉 x 迭代器:

// get right edge
for(y = 0; y < h; y++) {                       // line by line
    for(x = w; x >= 0; x--) {                  // from width to 0
        if (buffer32[x + y * w] > 0) {
            if (x > x2) x2 = x;
        }
    }
}

頂部和底部邊緣也是如此,只是迭代器顛倒了:

// get top edge
for(x = 0; x < w; x++) {
    for(y = 0; y < h; y++) {
        if (buffer32[x + y * w] > 0) {
            if (y < y1) y1 = y;
        }
    }
}

// get bottom edge
for(x = 0; x < w; x++) {
    for(y = h; y >= 0; y--) {
        if (buffer32[x + y * w] > 0) {
            if (y > y2) y2 = y;
        }
    }
}

結果區域為:

ctx.strokeRect(x1, y1, x2-x1, y2-y1);

您可以實施各種優化,但它們完全取決於場景,例如,如果您知道近似位置,則不必迭代所有行/列。

您可以通過跳過 x 個像素來對他的位置進行蠻力猜測,當您發現一個不透明的像素時,您可以基於該像素創建最大搜索區域等等,但這超出了此處的范圍。

希望這可以幫助!

就在最近,我需要類似的東西。 雖然問題得到了回答,但我想發布我的代碼以供將來參考。

就我而言,我正在空白/透明畫布上繪制一個(字體)圖標,並希望獲得邊界框。 即使我知道圖標的高度(使用字體大小,即高度),我也無法知道寬度。 所以我必須手動計算它。

我不確定是否有一種聰明的方法來計算這個。 我腦海中浮現的第一件事就是以艱難的方式去做:手動檢查每個像素,這就是我所做的。

我認為代碼是不言自明的,所以我不會做任何解釋。 我試圖保持代碼盡可能干凈。

 /* Layer 3: The App */ let canvas = document.querySelector("#canvas"); let input = document.querySelector("#input"); let output = document.querySelector("#output"); canvas.width = 256; canvas.height = 256; let context = canvas.getContext("2d"); context.font = "200px Arial, sans-serif"; let drawnLetter = null; drawLetter(input.value); function drawLetter(letter) { letter = letter ? letter[0] : null; if (!letter) { // clear canvas context.clearRect(0, 0, canvas.width, canvas.height); output.textContent = null; return; } if (letter == drawnLetter) { return; } drawnLetter = letter; // clear canvas context.clearRect(0, 0, canvas.width, canvas.height); // draw letter context.fillText(letter, 50, canvas.height - 50); // find edges let boundingBox = findEdges(context); // mark the edges context.beginPath(); context.rect(boundingBox.left, boundingBox.top, boundingBox.width, boundingBox.height); context.lineWidth = 2; context.strokeStyle = "red"; context.stroke(); // output the values output.textContent = JSON.stringify(boundingBox, null, " "); } /* Layer 2: Interacting with canvas */ function findEdges(context) { let left = findLeftEdge(context); let right = findRightEdge(context); let top = findTopEdge(context); let bottom = findBottomEdge(context); // right and bottom are relative to top left (0,0) return { left, top, right, bottom, width : right - left, height : bottom - top, }; } function findLeftEdge(context) { let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height); let emptyPixel = [0, 0, 0, 0].join(); for (let x = 0; x < context.canvas.width; x++) { for (let y = 0; y < context.canvas.height; y++) { let pixel = getPixel(imageData, x, y).join(); if (pixel != emptyPixel) { return x; } } } } function findRightEdge(context) { let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height); let emptyPixel = [0, 0, 0, 0].join(); for (let x = context.canvas.width - 1; x >= 0; x--) { for (let y = 0; y < context.canvas.height; y++) { let pixel = getPixel(imageData, x, y).join(); if (pixel != emptyPixel) { return x; } } } } function findTopEdge(context) { let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height); let emptyPixel = [0, 0, 0, 0].join(); for (let y = 0; y < context.canvas.height; y++) { for (let x = 0; x < context.canvas.width; x++) { let pixel = getPixel(imageData, x, y).join(); if (pixel != emptyPixel) { return y; } } } } function findBottomEdge(context) { let imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height); let emptyPixel = [0, 0, 0, 0].join(); for (let y = context.canvas.height - 1; y >= 0; y--) { for (let x = 0; x < context.canvas.width; x++) { let pixel = getPixel(imageData, x, y).join(); if (pixel != emptyPixel) { return y; } } } } /* Layer 1: Interacting with ImageData */ /** * Returns the pixel array at the specified position. */ function getPixel(imageData, x, y) { return getPixelByIndex(imageData, pos2index(imageData, x, y)); } /** * Returns the RGBA values at the specified index. */ function getPixelByIndex(imageData, index) { return [ imageData.data[index + 0], imageData.data[index + 1], imageData.data[index + 2], imageData.data[index + 3], ]; } /** * Returns the index of a position. */ function pos2index(imageData, x, y) { return 4 * (y * imageData.width + x); }
 body { background-color: hsl(0, 0%, 95%); } canvas { background: white; image-rendering: pixelated; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEXMzMz////TjRV2AAAAEUlEQVQI12P4z8CAFWEX/Q8Afr8P8erzE9cAAAAASUVORK5CYII=); zoom: 0.8; /* this counters the scale up (125%) of my screen; can be removed */ } input { padding: 0.2em; margin-top: 0.5em; }
 <canvas id="canvas"></canvas> <br> <input type="text" id="input" placeholder="type a letter" value="A" onkeyup="drawLetter(this.value)" /> <pre id="output"></pre>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM