opencv.js 透视变换 [英] opencv.js perspective transform

查看:96
本文介绍了opencv.js 透视变换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 opencv.js 在提供的图像中查找文档(检测边缘、应用透视变换等.

I'm trying to use opencv.js to find a document in a provided image (detect edges, apply perspective transform, etc.

我有一组合理的代码(偶尔)检测文档的边缘并为此抓取边界框.但是,我正在努力进行透视变换步骤.有一些帮助程序(不在 JS 中)这里此处.

I've got a reasonable set of code that (occasionally) detects edges of a document and grabs the bounding box for that. However, I'm struggling to do the perspective transform steps. There are some helpers for this (not in JS) here and here.

不幸的是,我遇到了一些简单的事情.我可以找到具有 4 个边的匹配 Mat.显示表明它是准确的.但是,我不知道如何从 Mat 中获取一些简单的 X/Y 信息.我认为 minMaxLoc() 将是一个不错的选择,但我一直在传递我匹配的 Mat 时遇到错误.知道为什么我可以绘制 foundContour 并从中获取边界框信息,但我不能在其上调用 minMaxLoc 吗?

Unfortunately I'm getting stuck on something simple. I can find the matching Mat that has 4 edges. Displaying that shows it to be accurate. However, I have no idea how to get some simple X/Y info out of that Mat. I thought minMaxLoc() would be a good option, but I keep getting an error passing in my matching Mat. Any idea why I can draw foundContour and get bounding box info from it, but I can't call minMaxLoc on it?

代码:

//<Get Image>
//<Convert to Gray, do GaussianBlur, and do Canny edge detection>
let contours = new cv.MatVector();
cv.findContours(matDestEdged, contours, hierarchy, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE);

//<Sort resulting contours by area to get largest>

let foundContour = null;
for (let sortableContour of sortableContours) {
  let peri = cv.arcLength(sortableContour.contour, true);
  let approx = new cv.Mat();
  cv.approxPolyDP(sortableContour.contour, approx, 0.1 * peri, true);

  if (approx.rows == 4) {
    console.log('found it');
    foundContour = approx
    break;
  }
  else {
    approx.delete();
  }
}

//<Draw foundContour and a bounding box to ensure it's accurate>

//TODO: Do a perspective transform
let result = cv.minMaxLoc(foundContour);

上面的最后一行导致运行时错误(Uncaught (in promise): 6402256 - Exception catch is disabled).我可以在其他 Mat 对象上运行 minMaxLoc().

The last line above results in a runtime error (Uncaught (in promise): 6402256 - Exception catching is disabled). I can run minMaxLoc() on other Mat objects.

推荐答案

对于希望在 OpenCV.JS 中执行此操作的其他任何人,我上面的评论似乎仍然是准确的.找到的轮廓不能与minMaxLoc一起使用,但可以从data32S[]中拉出X/Y数据.这应该是完成此透视变换所需的全部内容.部分代码如下.

For anyone else looking to do this in OpenCV.JS, what I commented above seems to still be accurate. The contour found can't be used with minMaxLoc, but the X/Y data can be pulled out of data32S[]. That should be all that's needed to do this perspective transform. Some code is below.

//Find all contours
let contours = new cv.MatVector();
let hierarchy = new cv.Mat();
cv.findContours(matDest, contours, hierarchy, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE);

//Get area for all contours so we can find the biggest
let sortableContours: SortableContour[] = [];
for (let i = 0; i < contours.size(); i++) {
  let cnt = contours.get(i);
  let area = cv.contourArea(cnt, false);
  let perim = cv.arcLength(cnt, false);

  sortableContours.push(new SortableContour({ areaSize: area, perimiterSize: perim, contour: cnt }));
}

//Sort 'em
sortableContours = sortableContours.sort((item1, item2) => { return (item1.areaSize > item2.areaSize) ? -1 : (item1.areaSize < item2.areaSize) ? 1 : 0; }).slice(0, 5);

//Ensure the top area contour has 4 corners (NOTE: This is not a perfect science and likely needs more attention)
let approx = new cv.Mat();
cv.approxPolyDP(sortableContours[0].contour, approx, .05 * sortableContours[0].perimiterSize, true);

if (approx.rows == 4) {
  console.log('Found a 4-corner approx');
  foundContour = approx;
}
else{
  console.log('No 4-corner large contour!');
  return;
}

//Find the corners
//foundCountour has 2 channels (seemingly x/y), has a depth of 4, and a type of 12.  Seems to show it's a CV_32S "type", so the valid data is in data32S??
let corner1 = new cv.Point(foundContour.data32S[0], foundContour.data32S[1]);
let corner2 = new cv.Point(foundContour.data32S[2], foundContour.data32S[3]);
let corner3 = new cv.Point(foundContour.data32S[4], foundContour.data32S[5]);
let corner4 = new cv.Point(foundContour.data32S[6], foundContour.data32S[7]);

//Order the corners
let cornerArray = [{ corner: corner1 }, { corner: corner2 }, { corner: corner3 }, { corner: corner4 }];
//Sort by Y position (to get top-down)
cornerArray.sort((item1, item2) => { return (item1.corner.y < item2.corner.y) ? -1 : (item1.corner.y > item2.corner.y) ? 1 : 0; }).slice(0, 5);

//Determine left/right based on x position of top and bottom 2
let tl = cornerArray[0].corner.x < cornerArray[1].corner.x ? cornerArray[0] : cornerArray[1];
let tr = cornerArray[0].corner.x > cornerArray[1].corner.x ? cornerArray[0] : cornerArray[1];
let bl = cornerArray[2].corner.x < cornerArray[3].corner.x ? cornerArray[2] : cornerArray[3];
let br = cornerArray[2].corner.x > cornerArray[3].corner.x ? cornerArray[2] : cornerArray[3];

//Calculate the max width/height
let widthBottom = Math.hypot(br.corner.x - bl.corner.x, br.corner.y - bl.corner.y);
let widthTop = Math.hypot(tr.corner.x - tl.corner.x, tr.corner.y - tl.corner.y);
let theWidth = (widthBottom > widthTop) ? widthBottom : widthTop;
let heightRight = Math.hypot(tr.corner.x - br.corner.x, tr.corner.y - br.corner.y);
let heightLeft = Math.hypot(tl.corner.x - bl.corner.x, tr.corner.y - bl.corner.y);
let theHeight = (heightRight > heightLeft) ? heightRight : heightLeft;

//Transform!
let finalDestCoords = cv.matFromArray(4, 1, cv.CV_32FC2, [0, 0, theWidth - 1, 0, theWidth - 1, theHeight - 1, 0, theHeight - 1]); //
let srcCoords = cv.matFromArray(4, 1, cv.CV_32FC2, [tl.corner.x, tl.corner.y, tr.corner.x, tr.corner.y, br.corner.x, br.corner.y, bl.corner.x, bl.corner.y]);
let dsize = new cv.Size(theWidth, theHeight);
let M = cv.getPerspectiveTransform(srcCoords, finalDestCoords)
cv.warpPerspective(matDestTransformed, finalDest, M, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar());

作为参考,这里是我用于 SortableContour 的类定义.然而,上面的代码仅作为指南,而不是可以自行运行的代码.

For reference, here is the class definition I was using for SortableContour. The code above is meant as a guide, not as something that can run on its own, however.

export class SortableContour {
    perimiterSize: number;
    areaSize: number;
    contour: any;
  
    constructor(fields: Partial<SortableContour>) {
      Object.assign(this, fields);
    }
  }

这篇关于opencv.js 透视变换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆