user889030
user889030

Reputation: 4764

How to calculate actual width and height of rotated object in canvas?

Am trying to calculate width and height of object i loaded into canvas. When object is not rotated i get correct left right top bottom values, but when i load rotated object in canvas then i not get correct values , so i wonder what will be the logic or math formula to do achieve it.

how am doing.

and then using min max xy value find corners of object which works with none rotated objects but not work with rotated/tilted objects.

so any idea how to solve this problem enter image description here

enter image description here

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

Upvotes: 2

Views: 687

Answers (2)

ZER0
ZER0

Reputation: 25332

If you work with some approximation, you can have something like that; I hope at least it can provide to you some ideas:

  // 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*/);

Here the link for testing: https://codepen.io/zer0/pen/zLxyQV

There are several problem with this approach: as said, with irregular images, it's not precise, in fact you will see the pin are making the image's bounding box a little bit smaller. But the thing can be worse: try in the link above to use the 2nd image, that is quite irregular, and you will see.

Of course we can compensate, using also a bit more complex algorithm instead this simple one, but the question is: what the expected result for something like the 2nd image? Depends by that you can decide how to proceed.

Upvotes: 1

madjaoue
madjaoue

Reputation: 5224

In some simple cases (like rectangular objects), you could try to rotate the image until you minimize the number of uncolored pixels.

So you start with your image, and for each of the possible 360°, you compute the ratio. This is not perfect, but "doable" simply in pure js.

Here's a pseudoCode that might help you:

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

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

Begining of an implementation (I edited your 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/

If this doesn't work, you could implement some image detection techniques (Mathematical morphology for example). But i think this is outside the scope of stackoverflow.

Upvotes: 1

Related Questions