为什么 OpenCV 中的 drawContour() 会生成这个奇怪的 Mask?
Why is the drawContour() in OpenCV generating this strange Mask?
我是从读这篇 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);
我得到了以下面具:
我期待的是黑色背景上的实心(白色)菱形。
为什么我会得到这个意想不到的结果?
编辑:
当我将 Mat mask = new Mat(submatrix.rows(),
submatrix.cols(), CvType.CV_8UC1);
替换为 Mat mask =
Mat.zeros(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);
时,
最后一个带有白色 garbage 的面具被替换为空的
黑色面具,上面没有任何白色。我得到了以下子垫
和面具:
我得到了等高线列表中的第一个等高线(名为
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()
之前)。那
导致这个子垫和掩码:
然后我改回了contours.get(0)
Imgproc.boundingRect()
;然后让
contourList.add(contours.get(1));
在那里。得到以下
底板和遮罩:
现在我完全无法理解这里发生的事情。
我不确定在 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()
-> 给你矩形的左上角
我是从读这篇 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);
我得到了以下面具:
我期待的是黑色背景上的实心(白色)菱形。
为什么我会得到这个意想不到的结果?
编辑:
当我将
Mat mask = new Mat(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);
替换为Mat mask = Mat.zeros(submatrix.rows(), submatrix.cols(), CvType.CV_8UC1);
时, 最后一个带有白色 garbage 的面具被替换为空的 黑色面具,上面没有任何白色。我得到了以下子垫 和面具:我得到了等高线列表中的第一个等高线(名为
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()
之前)。那 导致这个子垫和掩码:然后我改回了
contours.get(0)
Imgproc.boundingRect()
;然后让contourList.add(contours.get(1));
在那里。得到以下 底板和遮罩:
现在我完全无法理解这里发生的事情。
我不确定在 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()
-> 给你矩形的左上角