检测闭合轮廓

9

这是示例图片:

screenshot

我使用OpenCV来检测轮廓:

>>> fc = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
>>> contours = fc[0]

为了检测封闭轮廓,我打算在opencv返回的每个轮廓中检查起始点和结束点,但我注意到无论对象的形状如何,opencv似乎都会勾勒出每个对象,因此我得到了这个结果:

>>> for contour in contours:
>>>    print(contour[0,:,:], contour[-1,:,:])
[[246  38]] [[247  38]]
[[92 33]] [[93 33]]

每个找到的轮廓都有一个封闭路径。
我搜索了findContour()函数中可用方法的其他常量,但似乎所有返回的都是封闭路径。
那么是否有一般的方法来检测找到的轮廓是否封闭?

在提问之前我进行了谷歌搜索,但没有找到结果,但是我在类似问题的右侧看到了一个好的候选人:如何在OpenCV中知道轮廓是开放的还是封闭的?,其中建议使用cv2.isContourConvex(contour),但是:

>>> for contour in contours:
>>>    print(cv2.isContourConvex(contour))
False
False

又一个更新:contourArea 看起来可能提供答案(至少对于简单轮廓),但我没有在上面的示例图像以外的任何东西上进行测试:

>>> for contour in contours:
>>>     print(cv2.contourArea(contour))
0.0
12437.5

我不太确定,但你是否尝试使用convexHull()的输出来检查凸性?例如 contourhulled=cv2.convexHull(contour); cv2.isContourConvex(contourhulled) - Jason
4个回答

8

我自己遇到过这个问题,找到了解决的方法...

经历了这个过程...

for i in contours:
   print(cv2.contourArea(i), cv2.arcLength(i,True))

我注意到封闭轮廓(例如圆)的contourAreaarcLength高,而开放轮廓(例如线条)的contourAreaarcLength低, 因此您可以按此筛选它们...

closed_contours = []
open_contours = []
for i in contours:
    if cv2.contourArea(i) > cv2.arcLength(i, True):
        closed_contours.append(i)
    else:
        open_contours.append(i)

我知道这个问题是在3年前被问的,但对于其他需要想法的人来说,这对我有用!


1
如果您的示例图像是位图,即使一个像素也已经有了一个面积,因此它也有一个轮廓。封闭圆甚至有两个轮廓,一个在内部,一个在外部。半圆只有一个轮廓,跨越内部和外部,并在末端掉头。

我想你想把这两个圆当作曲线处理,实际上没有面积,只有一个“轮廓”。圆将成为封闭曲线,半圆将是开放的。如果是这样,您的新问题就是将位图转换为曲线。虽然我们人类很容易感知到曲线,但这并不是微不足道的,因为它需要定义一个算法和参数,将区域转换为曲线。

我知道的一种方法是从位图中派生出骨架,基本上是在外部剥离像素层,直到您只剩下一堆连接点。我不熟悉opencv,但我可以想象它已经具有一些用于此的实用程序。此外,搜索“curve line detect opencv”会出现opencv-identifying-lines-and-curves 作为第一个链接,以及其他一些命中。


0
这不是一个完全的Python答案,而且可能太晚了。从我的Emgu经验来看,任何面积为零的轮廓都不是封闭的。 您还可以将其面积与周长进行比较,作为额外的检查,如果非常小,则几乎肯定不是封闭的。不规则形状有时会在其轮廓的某个地方有一个微小的封闭区域。
轮廓面积可以从中获得。
cvContourArea

在Emgu中,轮廓周长被定义为

cvArcLength(contour, CV_WHOLE_SEQ, 1);

0

一个封闭的轮廓,根据定义,将会有一个单独的内部轮廓。
看一下findContours()函数的hierarchy参数:

hierarchy - 可选的输出向量,包含关于图像拓扑的信息。它有和轮廓数量一样多的元素。 对于每个第 i 个轮廓 contours [i],元素 hierarchy [i] [0]、 hierarchy i 、 hierarchy [i] [2] 和 hierarchy [i] [3] 被设置为在相同层级上的下一个和上一个轮廓、 第一个子轮廓和父轮廓相对于轮廓中的基于 0 的索引。如果对于轮廓 i 没有下一个、上一个、 父或嵌套轮廓,则 hierarchy [i] 的相应元素将为负数。


并不完全是这样的,正如您在我的示例(cv2.RETR_EXTERNAL)中所看到的,没有内部轮廓。无论如何,如果您可以从您引用的文档中推导出一种方法,请明确地呈现它,以便我们可以看到您的想法实施。谢谢。 - theta
轮廓永远不会有重复的点。这是设计上的考虑。尝试将图像膨胀一个像素或两个像素,以避免出现1像素厚的线条。这应该会给你一个内部轮廓。 - Adi Shavit
你所写的似乎不正确。无论膨胀如何,cv2.RETR_EXTERNAL始终只返回外部轮廓,而即使轮廓线宽为1像素,其他模式也始终会返回内部和外部轮廓。 - theta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接