代码之家  ›  专栏  ›  技术社区  ›  BRass

opencv.js透视转换

  •  0
  • BRass  · 技术社区  · 8 年前

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

    我有一套合理的代码(偶尔)可以检测文档的边缘并获取边界框。但是,我正在努力完成透视转换步骤。有一些帮手可以帮这个(不是在JS中) here here .

    不幸的是,我在做一些简单的事情。我能找到匹配的 Mat 有四个边。显示,显示它是准确的。但是,我不知道如何从中获得一些简单的X/Y信息 席子 . 我想 minMaxLoc() 这是个不错的选择,但我在比赛中不断出错 席子 . 知道我为什么会画画吗 foundContour 从中获取边界框信息,但我不能调用 minMaxLoc 在上面?

    代码:

    //<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 catching is disabled )我能跑 Mimax() 在其他 席子 物体。

    1 回复  |  直到 7 年前
        1
  •  3
  •   BRass    7 年前

    对于任何想在opencv.js中这样做的人,我上面的评论似乎仍然是准确的。找到的轮廓不能用于 minMaxLoc 但X/Y数据可以从 data32S[] . 这应该是进行透视转换所需要的全部内容。下面是一些代码。

    //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 . 上面的代码是作为一个指南,而不是作为一个可以自己运行的东西,然而。

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