在画布getImageData周围画一个框

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在画布getImageData周围画一个框相关的知识,希望对你有一定的参考价值。

我目前正在使用画布中的一些图像,我想在具有透明背景的图像周围绘制一个框(参见底部的示例)

我通过以下方式获取数据:context.getImageData(0, 0, canvas.width, canvas.height);

我的结果中只有黑色(不透明)数据;有了这个,我尝试了几个堆叠溢出器建议的marching squares algorithm,但我无法理解这一点。

我也试过骑自行车所有的数据,得到minXminYmaxXmaxY,所以我可以用这4点画一个盒子,但我不知道怎么样。 有什么建议吗?


答案

Logic steps

要扫描形状的边界框,您可以执行以下步骤。假设您从画布中提取了位图(ImageData),则使用xy迭代位图:

  1. 逐行扫描,从上到下扫描。首先找到实心像素,将当前y位置存储为y1,跳到下一步
  2. 逐行扫描从底部到y1。首先找到实心像素,将当前y位置存储为y2
  3. y1内从左到右水平扫描到y2。用画布宽度初始化x1。当在当前行上找到一个实心像素并且x的值低于当前的x1时,设置x1 =当前的x并跳到下一行
  4. 从右侧水平扫描到x1。用0初始化x2。当在当前行上找到一个实心像素并且x的值高于当前的x2时,设置x2 =当前的x并跳到下一行
  5. 面积的大小当然是:width = x2 - x1height = y2 - y1

消除锯齿的形状会影响尺寸。您可以包括检查实体的Alpha通道以减少此影响。

通过更新循环以使用新的x1 / x2值作为限制,可以针对左边缘和右边缘优化扫描。使用Uint32Array检查像素值。

Proof-of-concept

显示上述步骤结果的非优化实现。它将检查任何非alpha值。例如,如果要减少抗锯齿的影响,可以使用0x80000000替换0xff000000以检查> 127的alpha值。如果您没有alpha,只需检查实际颜色值(请注意,某些图像经过颜色校正,因此可以考虑容差)。

var ctx = document.querySelector("canvas").getContext("2d"),
    btn = document.querySelector("button"),
    w = ctx.canvas.width,
    h = ctx.canvas.height,
    img = new Image();
img.crossOrigin = ""; img.onload = plot; img.src = "//i.imgur.com/lfsyAEc.png";

btn.onclick = plot;

function plot() {
  var iw = img.width, ih = img.height,
      x = Math.random() * (w - iw * 0.5), y = Math.random() * (h - ih * 0.5),
      s = (Math.random() * 30 - 15)|0;
  ctx.clearRect(0, 0, w, h);
  ctx.translate(x, y);
  ctx.rotate(Math.random() * Math.PI - Math.PI * 0.5);
  ctx.drawImage(img, -(iw + s) * 0.5, -(ih + s) * 0.5, iw + s, ih + s);
  ctx.setTransform(1,0,0,1,0,0);
  analyze();
}

function analyze() {
  var data = new Uint32Array(ctx.getImageData(0, 0, w, h).data.buffer),
      len = data.length,
      x, y, y1, y2, x1 = w, x2 = 0;
  
  // y1
  for(y = 0; y < h; y++) {
    for(x = 0; x < w; x++) {
      if (data[y * w + x] & 0xff000000) {
        y1 = y;
        y = h;
        break;
      }
    }
  }

  //todo y1 and the others can be undefined if no pixel is found.
  
  // y2
  for(y = h - 1; y > y1; y--) {
    for(x = 0; x < w; x++) {
      if (data[y * w + x] & 0xff000000) {
        y2 = y;
        y = 0;
        break;
      }
    }
  }

  // x1
  for(y = y1; y < y2; y++) {
    for(x = 0; x < w; x++) {
      if (x < x1 && data[y * w + x] & 0xff000000) {
        x1 = x;
        break;
      }
    }
  }

  // x2
  for(y = y1; y < y2; y++) {
    for(x = w - 1; x > x1; x--) {
      if (x > x2 && data[y * w + x] & 0xff000000) {
        x2 = x;
        break;
      }
    }
  }
  
  // mark area:
  ctx.strokeStyle = "hsl(" + (360 * Math.random()) + ", 80%, 50%)";
  ctx.strokeRect(x1 + 0.5, y1 + 0.5, x2 - x1, y2 - y1);
}
body {background:#aaa;margin:1px 0}
canvas {border:1px solid #777; background:#f0f0f2}
<button>Again</button><br>
<canvas width=640 height=165></canvas>

以上是关于在画布getImageData周围画一个框的主要内容,如果未能解决你的问题,请参考以下文章

使用 getImageData 和 putImageData 进行 HTML5 画布缩放

如何删除画布周围的灰色边框

在画布上的图像不透明部分周围绘制边框

如何在 HTML5 画布周围拖动一段用户生成的文本?

遍历画布像素并找到每个白色像素的 x、y、位置

可以使用 getImageData / putImageData 之类的 html 图像吗?