1

我从阅读这个开始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);时,最后一个带有白色垃圾的蒙版被一个空的黑色蒙版替换,上面没有任何白色。我得到了以下子垫和掩码:

    在此处输入图像描述 在此处输入图像描述

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

    然后我继续更改contours.get(0)contours.get(1)inImgproc.boundingRect()以及 in contourList.add();(就在之前Imgproc.drawContours())。这导致了这个子垫和掩码:

    在此处输入图像描述 在此处输入图像描述

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

    在此处输入图像描述 在此处输入图像描述

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

4

1 回答 1

2

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

contours列表将包含一个点列表。这点将参考原始图像。所以,这意味着如果图一个在让我们说,x=300, y= 300, width= 100, height=100那么当你得到你的子矩阵时,它会尝试在较小的图像中绘制这些点......所以当它试图(300,300)在 100 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()-> 为您提供矩形的左上角

于 2016-04-20T08:38:01.663 回答