如何计算画布中旋转对象的实际宽度和高度?

时间:2018-07-11 12:23:21

标签: javascript canvas

我正在尝试计算我加载到画布中的对象的宽度和高度。当对象不旋转时,我得到正确的左右上底值,但是当我在画布中加载旋转后的对象时,我就没有正确的值,所以我想知道要实现它的逻辑或数学公式是什么。

最近怎么样。

  • 最初将图像加载到画布中
  • 从画布获取图像数据
  • 使用alpha检查循环浏览图像数据以仅获取彩色像素
  • 从彩色像素阵列中
  • 查找最小最大xy值

    var temp_ray = [];  // pixel array
    
        for (var y = 0; y < imgData.height; ++y) {
            for (var x = 0; x < imgData.width; ++x) {
                var index = (y * imgData.width + x) * 4; 
                if(imgData.data[index+3]){  
                    var xc = (index / 4) % imgData.width;   
                    var yc = Math.floor((index / 4) / imgData.width);  
                    temp_ray.push([xc,yc]);     
                }
    
            }
        }
        if(temp_ray.length > 0){
            var Xind = MaxMin2darray(temp_ray,0);
            var Yind = MaxMin2darray(temp_ray,1);
            var W = parseFloat(Xind['max']) - parseFloat(Xind['min']);
            var H = parseFloat(Yind['max']) - parseFloat(Yind['min']);  
            var center_x = Xind['min'] + (W/2);
            var center_y = Yind['min'] + (H/2);
            // find corners of object
    
            // find *min x , min y 
            let top_left = temp_ray[Xind['imin']];    // min X priority , min Y   // top left
            // find max x , *min y 
            let top_right = temp_ray[Yind['imin']];    // max X, min Y priority ,   // top right
            // find *max x , min y 
            let bot_right = temp_ray[Xind['imax']];    // max X priority , min Y  // bottom right 
            // find max x , *max y 
            let bot_left = temp_ray[Yind['imax']];    // max X , max Y priority // bottom left  
    
            var dim = {'W':W,'H':H,'CenterX':center_x,'CenterY':center_y,'top_left':top_left,'top_right':top_right,'bot_right':bot_right,'bot_left':bot_left,'Xend':Xind['max'],'Yend':Yind['max'],'Xstart':Xind['min'],'Ystart':Yind['min'],'Xend':Xind['max'],'Yend':Yind['max']};
            console.log(dim);
        }
    

然后使用min max xy值找到不旋转对象但不旋转对象的角。

所以任何想法如何解决这个问题 enter image description here

enter image description here

jsfiddle:http://jsfiddle.net/4L13vtaj/

2 个答案:

答案 0 :(得分:1)

在某些简单情况下(例如矩形对象),您可以尝试旋转图像,直到将未着色像素的数量最小化为止。

因此,从图像开始,并针对每个可能的360°计算比率。这不是完美的,但仅在纯js中“可行”。

这是一个伪代码,可以帮助您:

for degree in [0,365]{
  rotateOriginalImageBy(degree);
  cost[degree] = NemptyPixels/NfilledPixels;
}

predictedDegree = Math.min(cost);
rotateOriginalImageBy(predictedDegree);
compute 2 dimensions;
width = largerDimension;
height = shorterDimension;

开始实施(我编辑了您的jsfiddle):

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var rotatioDegree = 45;

var imageObject = new Image();
imageObject.onload = function() {
  var canvasWidth = imageObject.width;
  var canvasHeight = canvasWidth; // not useful since width==height
  document.getElementById('canvas').width = canvasWidth;
  document.getElementById('canvas').height = canvasWidth;
  ctx.clearRect(0, 0, canvasWidth, canvasWidth);
  // Move registration point to the center of the canvas
  ctx.translate(canvasWidth/2, canvasWidth/2)
  ctx.rotate(rotatioDegree*3.1415/180);
  ctx.translate(-canvasWidth/2,-canvasWidth/2)
  ctx.drawImage(imageObject,0,0);
  ctx.translate(canvasWidth/2, canvasWidth/2)
  ctx.rotate(-rotatioDegree*3.1415/180);
  ctx.translate(-canvasWidth/2,-canvasWidth/2)


  var imgData = ctx.getImageData(0, 0, canvasWidth, canvasWidth);

http://jsfiddle.net/4L13vtaj/17/

如果这不起作用,则可以实施一些图像检测技术(例如数学形态学)。但是我认为这超出了stackoverflow的范围。

答案 1 :(得分:1)

如果您使用某种近似值,则可以得到类似的结果;我希望至少它可以为您提供一些想法:

  // some pixels in this image are not transparent, so we add a tollerance
  // you can try to remove the second condition.
  const isNotEmpty = (color) => color && color < 0xffaaaaaa;

  function getTop(buff, w, h) {
    for (let y = 0; y < h; y++) {
      for (let x = 0; x < w; x++) {
        let i = y * w + x;
        if (isNotEmpty(buff[i])) {
          return {x, y}
        }
      }
    }
  }

  function getRight(buff, w, h) {
      for (let x = w; x >=0; x--) {
        for (let y = 0; y < h; y++) {
          let i = y * w + x;
          if (isNotEmpty(buff[i])) {
            return {x, y}
          }
      }
    }
  }

  function getBottom(buff, w, h) {
    for (let y = h; y >= 0; y--) {
      for (let x = 0; x < w; x++) {
        let i = y * w + x;
        if (isNotEmpty(buff[i])) {
          return {x, y}
        }
      }
    }
  }

  function getLeft(buff, w, h) {
      for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
          let i = y * w + x;
          if (isNotEmpty(buff[i])) {
            return {x, y}
          }
      }
    }
  }

  async function main(imageSource) {
    const  canvas = document.querySelector("canvas");
    const ctx = canvas.getContext("2d");

    const imageObject = new Image();
    imageObject.src = imageSource;
    await new Promise(r => imageObject.onload = r);

    const w = canvas.width = imageObject.width;
    const h = canvas.height = imageObject.height;

    ctx.clearRect(0, 0, w, h);
    ctx.drawImage(imageObject, 0, 0);

    const imgData = ctx.getImageData(0, 0, w, h);
    const buff = new Uint32Array(imgData.data.buffer);

    const points = [
      getTop(buff, w, h),
      getRight(buff, w, h),
      getBottom(buff, w, h),
      getLeft(buff, w, h)
    ];

    ctx.strokeStyle = "#0000ff"
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    ctx.lineTo(points[1].x, points[1].y);
    ctx.lineTo(points[2].x, points[2].y);
    ctx.lineTo(points[3].x, points[3].y);
    ctx.closePath();
    ctx.stroke();
  }

  main(/* image's url*/);

这里是测试链接:https://codepen.io/zer0/pen/zLxyQV

这种方法存在几个问题:如上所述,对于不规则的图像,它并不精确,实际上您会看到图钉使图像的边界框变小了。 但是情况可能更糟:尝试使用上面的链接使用第二张图像,这很不规则,您会看到的。

我们当然可以补偿,也可以使用更复杂的算法代替这个简单的算法,但是问题是:像第二张图片一样的预期结果是什么?取决于您可以决定如何进行。