我正在寻找一种检测透明 PNG 中形状的方法。 例如,我将创建一个 940x680 的透明 Canvas ,然后在该 Canvas 的某处放置一个完全不透明的对象。
我希望能够检测到该对象的大小 (w, h) 和顶部 + 左侧位置。
这是原始图像的示例:
这是我想要实现的示例(边界框叠加,顶部 + 左边距数据):
我找到了一种可以进行透明度检测的资源,但我不确定如何将类似的东西扩展到我正在寻找的东西。
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 边缘
这些扫描可以合并,但为简单起见,我将分别显示每个步骤。
Online demo of this can be found here.
结果:
当图像加载时绘制它(如果图像很小,那么这个例子的其余部分将是浪费,因为你在绘制它时会知道坐标 - 假设你绘制的图像很大,里面有一个小图像)
(注意:为了简单起见,这是一个未优化的版本)
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 个像素来蛮力猜测他的位置,当您发现一个不透明的像素时,您可以根据该像素等建立最大搜索区域,但这超出了这里的范围。
希望这对您有所帮助!
关于javascript - 如何检测透明 Canvas 上的形状?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23255825/