Javascript用于检测不透明的PNG区域的方法

Mik*_*ike 5 javascript html5 png canvas html5-canvas

我正在寻找一种在透明PNG中检测图像的javascript方法.例如,我将使用940x680的透明画布创建一个PNG,然后在该画布中的某处放置一个完整的不透明对象.

我希望能够检测到画布中不透明的对象的大小(h/w)和顶部+左侧位置

这是原始图像的示例带图像对象的透明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);
}
Run Code Online (Sandbox Code Playgroud)

小提琴

小智 9

你需要做什么:

  • 获取缓冲区
  • 获取该缓冲区的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
Run Code Online (Sandbox Code Playgroud)

然后扫描每个边缘.对于左边缘,每行扫描从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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于右边缘,您只需反转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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于顶部和底部边缘也是如此,只是迭代器是相反的:

// 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果区域是:

ctx.strokeRect(x1, y1, x2-x1, y2-y1);
Run Code Online (Sandbox Code Playgroud)

您可以实现各种优化,但它们完全取决于场景,例如,如果您知道大致的放置,那么您不必迭代所有行/列.

你可以通过跳过x个像素来做一个蛮力猜测他的位置,当你发现一个非透明像素你可以根据它创建一个最大搜索区域等等,但这不在这里.

希望这可以帮助!