简体   繁体   English

了解轮廓层次结构:如何在 OpenCV 中区分实心圆/轮廓和未填充圆/轮廓?

[英]Understanding contour hierarchies: How to distinguish filled circle/contour and unfilled circle/contour in OpenCV?

I am unable to differentiate between the below two contours.我无法区分以下两个轮廓。 cv2.contourArea() is giving the same value for both. cv2.contourArea()为两者提供相同的值。 Is there any function to distinguish them in Python? Python中有什么函数可以区分它们吗? How do I use contour hierarchies to determine the difference?如何使用轮廓层次结构来确定差异?

图:示例圆/轮廓

To distinguish between a filled contour and unfilled contour, you can use contour hierarchy when finding contours with cv2.findContours() .要区分填充轮廓和未填充轮廓,可以在使用cv2.findContours()查找轮廓时使用轮廓层次结构。 Specifically, you can select the contour retrieval mode to optionally return an output vector containing information about the image topology.具体来说,您可以选择轮廓检索模式,以选择性地返回包含有关图像拓扑信息的输出向量。 There are the four possible modes:有四种可能的模式:

  • cv2.RETR_EXTERNAL - retrieves only the extreme outer contours (no hierarchy) cv2.RETR_EXTERNAL - 仅检索极端外部轮廓(无层次结构)
  • cv2.RETR_LIST - retrieves all of the contours without establishing any hierarchical relationships cv2.RETR_LIST - 检索所有轮廓而不建立任何层次关系
  • cv2.RETR_CCOMP - retrieves all of the contours and organizes them into a two-level hierarchy. cv2.RETR_CCOMP - 检索所有轮廓并将它们组织成两级层次结构。 At the top level, there are external boundaries of the components.在顶层,有组件的外部边界。 At the second level, there are boundaries of the holes.在第二层,有孔的边界。 If there is another contour inside a hole of a connected component, it is still put at the top level如果连接组件的孔内有另一个轮廓,它仍然放在顶层
  • cv2.RETR_TREE - retrieves all of the contours and reconstructs a full hierarchy of nested contours cv2.RETR_TREE - 检索所有轮廓并重建嵌套轮廓的完整层次结构

Understanding contour hierarchies了解轮廓层次结构

So with this information, we can use cv2.RETR_CCOMP or cv2.RETR_TREE to return a hierarchy list.因此有了这些信息,我们可以使用cv2.RETR_CCOMPcv2.RETR_TREE返回一个层次结构列表。 Take for example this image:以这张图片为例:

When we use the cv2.RETR_TREE parameter, the contours are arranged in a hierarchy, with the outermost contours for each object at the top.当我们使用cv2.RETR_TREE参数时,轮廓是按层次排列的,每个对象的最外层轮廓位于顶部。 Moving down the hierarchy, each new level of contours represents the next innermost contour for each object.向下移动层次结构,每个新的轮廓级别代表每个对象的下一个最里面的轮廓。 In the image above, the contours in the image are colored to represent the hierarchical structure of the returned contours data.在上图中,图像中的轮廓被着色以表示返回的轮廓数据的层次结构。 The outermost contours are red, and they are at the top of the hierarchy.最外面的轮廓是红色的,它们位于层次结构的顶部。 The next innermost contours -- the dice pips, in this case -- are green.下一个最里面的轮廓——在这种情况下是骰子点——是绿色的。

We can get that information about the contour hierarchies via the hierarchy array from the cv2.findContours function call.我们可以通过cv2.findContours函数调用中的层次结构数组获取有关轮廓层次结构的信息。 Suppose we call the function like this:假设我们这样调用函数:

(_, contours, hierarchy) = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

The third return value, saved in the hierarchy variable in this code, is a three-dimensional NumPy array, with one row, X columns, and a "depth" of 4. The X columns correspond to the number of contours found by the function.第三个返回值,保存在这段代码中的hierarchy变量中,是一个三维NumPy数组,有一行, X列,“深度”为X列对应函数找到的轮廓数. The cv2.RETR_TREE parameter causes the function to find the internal contours as well as the outermost contours for each object. cv2.RETR_TREE参数使函数找到每个对象的内部轮廓以及最外层轮廓。 Column zero corresponds to the first contour, column one the second, and so on.零列对应于第一个轮廓,第一列对应于第二个,依此类推。

Each of the columns has a four-element array of integers, representing indices of other contours, according to this scheme:根据此方案,每一列都有一个四元素整数数组,表示其他轮廓的索引:

[next, previous, first child, parent]

The next index refers to the next contour in this contour's hierarchy level, while the previous index refers to the previous contour in this contour's hierarchy level.下一个索引是指该轮廓层次级别中的下一个轮廓,而前一个索引是指该轮廓层次级别中的前一个轮廓。 The first child index refers to the first contour that is contained inside this contour.第一个子索引是指包含在此轮廓内的第一个轮廓。 The parent index refers to the contour containing this contour.索引是指包含此轮廓的轮廓。 In all cases, an value of -1 indicates that there is no next , previous , first child , or parent contour, as appropriate.在所有情况下,值-1表示没有nextpreviousfirst childparent轮廓,视情况而定。 For a more concrete example, here are some example hierarchy values.对于更具体的示例,以下是一些示例hierarchy值。 The values are in square brackets, and the indices of the contours precede each entry.这些值在方括号中,轮廓的索引在每个条目之前。 If you printed out the hierarchy array you will get something like this如果你打印出层次结构数组,你会得到这样的东西

0:  [ 6 -1  1 -1]   18: [19 -1 -1 17]
1:  [ 2 -1 -1  0]   19: [20 18 -1 17]
2:  [ 3  1 -1  0]   20: [21 19 -1 17]
3:  [ 4  2 -1  0]   21: [22 20 -1 17]
4:  [ 5  3 -1  0]   22: [-1 21 -1 17]
5:  [-1  4 -1  0]   23: [27 17 24 -1]
6:  [11  0  7 -1]   24: [25 -1 -1 23]
7:  [ 8 -1 -1  6]   25: [26 24 -1 23]
8:  [ 9  7 -1  6]   26: [-1 25 -1 23]
9:  [10  8 -1  6]   27: [32 23 28 -1]
10: [-1  9 -1  6]   28: [29 -1 -1 27]
11: [17  6 12 -1]   29: [30 28 -1 27]
12: [15 -1 13 11]   30: [31 29 -1 27]
13: [14 -1 -1 12]   31: [-1 30 -1 27]
14: [-1 13 -1 12]   32: [-1 27 33 -1]
15: [16 12 -1 11]   33: [34 -1 -1 32]
16: [-1 15 -1 11]   34: [35 33 -1 32]
17: [23 11 18 -1]   35: [-1 34 -1 32]

The entry for the first contour is [6, -1, 1, -1] .第一个轮廓的条目是[6, -1, 1, -1] This represents the first of the outermost contours;这代表最外层轮廓中的第一个; note that there is no particular order for the contours, eg, they are not stored left to right by default.请注意,轮廓没有特定的顺序,例如,默认情况下它们不会从左到右存储。 The entry tells us that the next dice outline is the contour with index six, that there is no previous contour in the list, that the first contour inside this one has index one, and that there is no parent for this contour (no contour containing this one).该条目告诉我们下一个骰子轮廓是索引为 6 的轮廓,列表中没有前一个轮廓,该轮廓内的第一个轮廓具有索引 1,并且该轮廓没有父轮廓(没有包含这个)。 We can visualize the information in the hierarchy array as seven trees, one for each of the dice in the image.我们可以将hierarchy数组中的信息可视化为七棵树,图像中的每个骰子一棵。

在此处输入图像描述

The seven outermost contours are all those that have no parent, ie, those with an value of -1 in the fourth field of their hierarchy entry.最外面的七个轮廓都是没有父轮廓的轮廓,即在其hierarchy条目的第四个字段中值为-1的轮廓。 Each of the child nodes beneath one of the "roots" represents a contour inside the outermost contour. “根”之一下方的每个子节点代表最外轮廓内的轮廓。 Note how contours 13 and 14 are beneath contour 12 in the diagram.请注意等高线 13 和 14 如何位于图中的等高线 12 下方。 These two contours represent the innermost contours, perhaps noise or some lost paint in one of the pips.这两个轮廓代表最里面的轮廓,可能是其中一个点中的噪声或一些丢失的油漆。 Once we understand how contours are arranged into a hierarchy, we can perform more sophisticated tasks, such as counting the number of contours within a shape in addition to the number of objects in an image.一旦我们了解了轮廓是如何排列成层次结构的,我们就可以执行更复杂的任务,例如除了计算图像中对象的数量之外,还计算形状中的轮廓数量。


Going back to your question, we can use hierarchy to distinguish between inner and outer contours to determine if a contour is filled or unfilled.回到您的问题,我们可以使用层次结构来区分内轮廓和外轮廓,以确定轮廓是填充还是未填充。 We can define a filled contour as a contour with no child whereas a unfilled contour as at least one child.我们可以将填充轮廓定义为没有子元素的轮廓,而将未填充轮廓定义为至少一个子元素。 So with this screenshot of your input image (removed the box):因此,使用此输入图像的屏幕截图(删除框):

在此处输入图像描述

Result结果

在此处输入图像描述

Code代码

import cv2

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Filter using contour hierarchy
cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
hierarchy = hierarchy[0]
for component in zip(cnts, hierarchy):
    currentContour = component[0]
    currentHierarchy = component[1]
    x,y,w,h = cv2.boundingRect(currentContour)
    # Has inner contours which means it is unfilled
    if currentHierarchy[3] > 0:
        cv2.putText(image, 'Unfilled', (x,y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)
    # No child which means it is filled
    elif currentHierarchy[2] == -1:
        cv2.putText(image, 'Filled', (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)

cv2.imshow('image', image)
cv2.waitKey()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM