skip to Main Content

I’m having difficulties in orienting my transformed image using OpenCV (JS).
Basically, I upload an image, select four points of the image (four corners), and transform it to be shown from a bird’s eye-view.

If I purposefully select the points from top-left, top-right, bottom-right, bottom-left, the orientation of the transformed image will be the same as the original image.
However, if I select the points from let’s say bottom-right, going counter-clockwise, the orientation flips.

What are some ways I can keep the source points as [[top-left x,y], [top-right x,y], [bottom-right x,y], [bottom-left x,y] ]?

The array of numbers i get back from my source points is likeso:

const srcPoints = [261, 35.5, 514, 23.5, 677, 487.5, 181, 538.5];

here is my entire transformation logic:

export function transformPerspective({
  originalImage,
  padding,
  srcPoints,
}: TransformPerspective): Promise<HTMLImageElement> {
  return new Promise((resolve) => {
    let img = new Image();
    img.id = "konvaImage";
    img.src = originalImage;
    img.style.display = "none";
    document.body.appendChild(img);

    img.onload = () => {
      // here the srcPoints need to be sorted in the top-left/top-right/bottom-right/bottom-left order
      let srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, srcPoints);
      const dstWidth = 900;
      const dstHeight = 1000;

      let dstTri = cv.matFromArray(4, 1, cv.CV_32FC2, [
        padding,
        padding,
        dstWidth - padding,
        padding,
        dstWidth - padding,
        dstHeight - padding,
        padding,
        dstHeight - padding,
      ]);

      let matrix = cv.getPerspectiveTransform(srcTri, dstTri);
      let dsize = new cv.Size(dstWidth, dstHeight);
      let myimg = cv.imread(
        document.querySelector("#konvaImage") as HTMLImageElement
      );
      let warpedImage = new cv.Mat();
      cv.warpPerspective(myimg, warpedImage, matrix, dsize);

      const outputCanvas = document.createElement("canvas");
      outputCanvas.id = "outputCanvas";
      outputCanvas.width = 900;
      outputCanvas.height = 1000;
      outputCanvas.style.display = "none";
      document.body.append(outputCanvas);

      cv.imshow("outputCanvas", warpedImage);
      const imageFromCanvas = new window.Image();
      imageFromCanvas.src = outputCanvas.toDataURL();

      // cleanup
      myimg.delete();
      warpedImage.delete();
      matrix.delete();
      srcTri.delete();
      dstTri.delete();
      outputCanvas.remove();

      // used to update my canvas image
      resolve(imageFromCanvas);
    };
  });
}

any ideas?

2

Answers


  1. Chosen as BEST ANSWER

    A kind samaritan helped me out with this, coming up with centroid of points, and getting it by getting the average of the four points. Then we can get the calculated angles. then sort angles array based on the angle value, which can sort the points in the counter-clockwise direction with respect to the centroid, which is what I needed.

    might have been a bit complex, maybe there is another way?

    but it works for now:

    
    export function sortTransformPoints(points: number[]) {
      // centroid of the points
      let cx = 0,
        cy = 0;
      for (let i = 0; i < points.length; i += 2) {
        cx += points[i];
        cy += points[i + 1];
      }
      cx /= 4;
      cy /= 4;
    
      // angles of each point from the centroid
      let angles = [];
      for (let i = 0; i < points.length; i += 2) {
        let dx = points[i] - cx;
        let dy = points[i + 1] - cy;
        let ang = Math.atan2(dy, dx);
        angles.push({ index: i, angle: ang });
      }
    
      // sort points by angle
      angles.sort((a, b) => a.angle - b.angle);
    
      let orderedPts = [];
      for (let ang of angles) {
        orderedPts.push(points[ang.index], points[ang.index + 1]);
      }
    
      return orderedPts;
    }
    

  2. Just create a helper function to sort the source points in the top-left/top-right/bottom-right/bottom-left order:

    function sortSourcePoints(srcPoints) {
      const sortedPoints = [...srcPoints];
      sortedPoints.sort((a, b) => {
        if (a < b) return -1;
        if (a > b) return 1;
        return 0;
      });
    
      const [topLeft, topRight, bottomRight, bottomLeft] = sortedPoints;
      return [topLeft, topRight, bottomRight, bottomLeft];
    }
    
    

    Then, update your transformPerspective function to use the sorted source points:

    export function transformPerspective({
      originalImage,
      padding,
      srcPoints,
    }: TransformPerspective): Promise<HTMLImageElement> {
      return new Promise((resolve) => {
        // ...
    
        img.onload = () => {
          const sortedSrcPoints = sortSourcePoints(srcPoints);
    
          // ...
    
          let srcTri = cv.matFromArray(4, 1, cv.CV_32FC2, sortedSrcPoints);
    
          // ...
        };
    
        // ...
      });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search