25

如何在opencv中检测开放和封闭的形状。

在此处输入图像描述

这些是我想要检测的简单样本形状。我已经使用findContoursandapproxPolyDP和而不是检查向量之间的角度来检测矩形。

现在我想检测开放形状,approxPolyDP函数将封闭形状的 bool 设置为 true,并且还检查isCounterConvex返回的点,加上contourArea限制。

任何想法我应该如何继续检测这类图像。

4

5 回答 5

34

只需在图像中使用findContours(),然后通过检查传递给 findContours() 函数的层次结构来确定轮廓是否闭合。从第二幅图中可以清楚地看出,与第一幅图像相比,没有轮廓具有子轮廓,您将从层次参数中获得此数据,该参数是可选的输出向量,包含有关图像拓扑的信息。它具有与轮廓数一样多的元素。

在这里,我们将使用层次结构作为

vector< Vec4i > hierarchy

第 i 个轮廓在哪里

hierarchy[i][0] = next contour at the same hierarchical level
hierarchy[i][1] = previous contour at the same hierarchical level
hierarchy[i][2] = denotes its first child contour
hierarchy[i][3] = denotes index of its parent contour

如果轮廓 i 没有下一个、上一个、父级或嵌套轮廓,则对应的元素hierarchy[i]将为负数。有关详细信息,请参阅findContours()函数。

因此,通过检查该值hierarchy[i][2],您可以确定轮廓是否属于关闭,即如果hierarchy[i][2] = -1没有子项并且它属于打开的轮廓。

还有一件事是在 findContours() 函数中,您应该使用 CV_RETR_CCOMP 检索所有轮廓并将它们组织成两级层次结构。

这是如何实现这一点的 C++ 代码。

    Mat tmp,thr;
    Mat src=imread("1.png",1);
    cvtColor(src,tmp,CV_BGR2GRAY);
    threshold(tmp,thr,200,255,THRESH_BINARY_INV);

    vector< vector <Point> > contours; // Vector for storing contour
    vector< Vec4i > hierarchy;
    findContours( thr, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

    for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour.
    {
        Rect r= boundingRect(contours[i]);
        if(hierarchy[i][2]<0) //Check if there is a child contour
          rectangle(src,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,0,255),2,8,0); //Opened contour
        else
          rectangle(src,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,255,0),2,8,0); //closed contour
    }

结果:

在此处输入图像描述

于 2014-03-07T05:24:50.630 回答
18

虽然对所提出的问题是正确的,但 @Haris 有用的答案不应被视为使用findContours()识别闭合轮廓的一般解决方案。

一个原因是填充的对象将没有内部轮廓,因此会返回hierarchy[i][2] = -1,这意味着该测试本身会错误地将此类轮廓标记为“开放”。

填充对象的轮廓在轮廓层次结构中不应有子级或父级,即位于顶层。因此,要检测填充对象的闭合轮廓至少需要额外的测试:if(hierarchy[i][2] < 0 && hierarchy[i][3] < 0).

我认为@Haris 的回答可能暗示了这一点,但我认为对于像我这样正在学习如何使用 opencv 的人来说,值得澄清一下。

于 2017-03-03T18:22:07.743 回答
9

Python 实现与下面相同。

import cv2


src = cv2.imread('test.png', cv2.IMREAD_COLOR)

#Transform source image to gray if it is not already
if len(src.shape) != 2:
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
else:
    gray = src

ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy[0]

for i, c in enumerate(contours):
    if hierarchy[i][2] < 0 and hierarchy[i][3] < 0:
        cv2.drawContours(src, contours, i, (0, 0, 255), 2)
    else:
        cv2.drawContours(src, contours, i, (0, 255, 0), 2)
#write to the same directory
cv2.imwrite("result.png", src)
于 2020-01-15T09:45:26.690 回答
1

答案取决于您的图像,更具体地说,预设了多少轮廓,是否有其他对象,噪声等。在一个简单的情况下,在闭合轮廓内部开始的单个轮廓泛洪填充不会溢出整个图像; 如果从外面开始,它不会进入中间。所以你会在这两种情况下保留一些白色区域。

于 2014-03-07T05:37:43.403 回答
0

上面的简化 Python 代码

import cv2

# get contours from image
img = cv2.imread("image.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
thresh, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# draw all contours to image (green if opened else red)
for i in range(len(contours)):
    opened = hierarchy[0][i][2]<0 and hierarchy[0][i][3]<0
    cv2.drawContours(img, contours, i, (0,255,0) if opened else (0,0,255), 2)

cv2.imshow("Contours", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
于 2021-10-19T17:25:28.900 回答