【问题标题】:Why is the drawContour() in OpenCV generating this strange Mask?为什么 OpenCV 中的 drawContour() 会生成这个奇怪的 Mask?
【发布时间】:2016-04-20 05:34:00
【问题描述】:

我首先阅读了这个Mat

然后我将其转换为灰度并对其应用Imgproc.canny(),得到以下蒙版。

然后我用Imgproc.findContours()找到轮廓,Imgproc.drawContours()Core.putText()用数字标记轮廓:

然后我做了Rect boundingRect = Imgproc.boundingRect(contours.get(0)); Mat submatrix = new Mat(); submatrix = originalMat.submat(boundingRect); 来关注submatrix

到目前为止一切顺利。问题从今以后开始:

现在我需要 submatrix 的面具。所以我决定用Imgproc.drawContours()来获取面具:

Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);
        List<MatOfPoint> contourList = new ArrayList<>();
        contourList.add(contours.get(0));
        Imgproc.drawContours(mask, contourList, 0, new Scalar(255), -1);

我得到了以下面具:

我期待的是黑色背景上的填充(白色)菱形。

为什么我会得到这个意想不到的结果?


编辑:

  1. 当我将Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1); 替换为Mat mask = Mat.zeros(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1); 时, 最后一个带有白色 garbage 的掩码被替换为空的 黑色面具上没有任何白色。我得到了以下子垫 和面具:

  2. 我得到了轮廓列表中的第一个轮廓(命名为 contours) 通过contours.get(0),并使用第一个轮廓 计算Imgproc.boundingRect() 以及 contourList.add(contours.get(0)); 稍后(contourList 是 将在最后使用的仅一个轮廓的列表 drawContours())。

    然后我继续将contours.get(0) 更改为 contours.get(1)Imgproc.boundingRect()contourList.add(); 中(就在Imgproc.drawContours() 之前)。那 产生了这个子垫和掩码:

  3. 然后我改回contours.get(0) in Imgproc.boundingRect();然后让 contourList.add(contours.get(1)); 在那里。得到以下 子垫和掩码:

现在我完全无法理解这里发生了什么。

【问题讨论】:

    标签: opencv image-processing opencv3.0 opencv4android opencv-contour


    【解决方案1】:

    我不确定这是如何在 JAVA 中处理的(我通常在 c++ 或 python 中使用 OpenCV),但是您的代码中有错误...

    contours 列表将包含一个点列表。这点将参考原始图像。所以,这意味着如果图一个在让我们说,x=300, y= 300, width= 100, height=100 那么当你得到你的子矩阵时,它会尝试在一个较小的图像中绘制这些点......所以当它试图在 100 中绘制点 (300,300) x 100 图像,它只会失败...可能会引发错误或根本不绘制任何东西...

    对此的解决方案是,执行一个 for 循环并在轮廓的每个点上减去边界矩形的初始点(在我的示例中为 (300,300))。

    作为,为什么会有一些垃圾绘制......好吧,你永远不会初始化矩阵。在 JAVA 中不确定,但在 c++ 中你必须将它们设置为 0。 我认为应该是这样的:

    Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1, new Scalar(0));
    

    我希望这会有所帮助:)

    编辑

    我想我之前没有解释清楚。

    您的轮廓是一个点数组 (x,y)。这些是代表原始图像中每个轮廓的点的坐标。该图像具有大小,而您的子矩阵具有较小的大小。这些点在这个小图像边界之外......

    你应该这样做来修复它:

    for (int j = 0; j < contours[0].length; j++) {
        contours[0][j].x -= boundingrect.x;
        contours[0][j].y -= boundingrect.y;
    }
    

    然后您可以绘制轮廓,因为它们将位于子垫的边界内。

    我认为在java中也可以直接减去opencv点:

    for (int j = 0; j < contours[0].length; j++) {
        contours[0][j] -= boundingrect.tl();
    }
    

    但在这种情况下我不确定,因为我只在 c++ 中尝试过

    boundingrect.tl() -> 为您提供矩形的左上角

    【讨论】:

    • 我试过new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1, new Scalar(0));,但没有奏效。但是Mat.zeros() 有效。我尝试了其他一些事情,这让我更加困惑。你能看到我问题底部的编辑吗?
    • 非常感谢您解决了我的问题。我无法完全理解最初的答案,但感谢您在编辑中澄清。在Java中,我必须将轮廓(其类型为MatOfPoint)转换为Points的数组,然后我操作每个Pointxy坐标:Point[] pointsArray = contours.get(0).toArray(); for (int pointIndex = 0; pointIndex &lt; pointsArray.length; pointIndex++) { pointsArray[pointIndex].x -= boundingRect.x; pointsArray[pointIndex].y -= boundingRect.y; } contours.get(0).fromArray(pointsArray);
    • @Solace 没问题,我的 Java 有点生疏了,所以我尽我所能写了……我很高兴它成功了 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    • 1970-01-01
    • 1970-01-01
    • 2018-03-06
    相关资源
    最近更新 更多