Python+OpenCV:图像轮廓

机器视觉001 2020-11-16 01:29:17
Python OpenCV 图像 python+opencv 轮廓


Python+OpenCV:图像轮廓

轮廓是什么?

轮廓可以简单地解释为一条连接所有连续点(沿边界)的曲线,具有相同的颜色和强度。

轮廓线是形状分析、目标检测和识别的重要工具。

为了获得更好的精度,可以使用二进制图像。所以在找到轮廓之前,应该应用阈值或canny边缘检测。

从OpenCV 3.2开始,findcontour()不再修改源图像。

在OpenCV中,寻找轮廓就像从黑色背景中寻找白色对象一样。所以记住,要找到的对象应该是白色的,背景应该是黑色的。

让我们来看看如何找到二值图像的轮廓:

####################################################################################################
# 图像轮廓(Image Contours)
def lmc_cv_image_contours():
"""
函数功能: 图像轮廓(Image Contours).
"""
# 读取图像
image = lmc_cv.imread('D:/99-Research/Python/Image/Box2.png')
contours_image1 = image.copy()
contours_image2 = image.copy()
gray_image = lmc_cv.cvtColor(image, lmc_cv.COLOR_BGR2GRAY)
# 图像轮廓(Image Contours)
ret, binary_image = lmc_cv.threshold(gray_image, 127, 255, lmc_cv.THRESH_BINARY)
contours1, hierarchy1 = lmc_cv.findContours(binary_image, mode=lmc_cv.RETR_TREE, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
contours2, hierarchy2 = lmc_cv.findContours(binary_image, mode=lmc_cv.RETR_TREE, method=lmc_cv.CHAIN_APPROX_SIMPLE,
offset=(0, 0))
lmc_cv.drawContours(contours_image1, contours1, -1, (0, 0, 255), 3)
lmc_cv.drawContours(contours_image2, contours2, -1, (0, 0, 255), 3)
points_number1 = 0
for i in range(len(contours1)):
points_number1 = points_number1 + len(contours1[i])
points_number2 = 0
for i in range(len(contours2)):
points_number2 = points_number2 + len(contours2[i])
# 显示图像
pyplot.figure('Image Display')
titles = ['Original Image', '%d Points in All Contours(%d)' % (points_number1, len(contours1)),
'%d Points in Simple Contours(%d)' % (points_number2, len(contours2))]
images = [image, contours_image1, contours_image2]
for i in range(3):
pyplot.subplot(1, 3, i + 1)
pyplot.imshow(images[i], 'gray')
pyplot.title(titles[i])
pyplot.xticks([])
pyplot.yticks([])
pyplot.show()
# 根据用户输入保存图像
if ord("q") == (lmc_cv.waitKey(0) & 0xFF):
# 销毁窗口
pyplot.close()
return

 

轮廓特征(Contour Features)

####################################################################################################
# 图像轮廓特征(Image Contour Features)
def lmc_cv_image_contours_features():
"""
函数功能: 图像轮廓特征(Image Contour Features).
"""
# 读取图像
image = lmc_cv.imread('D:/99-Research/Python/Image/Box2-2.png')
contours_image1 = image.copy()
contours_image2 = image.copy()
contours_image3 = image.copy()
contours_image4 = image.copy()
contours_image5 = image.copy()
contours_image6 = image.copy()
contours_image7 = image.copy()
contours_image8 = image.copy()
gray_image = lmc_cv.cvtColor(image, lmc_cv.COLOR_BGR2GRAY)
# 图像轮廓特征(Image Contour Features)
ret, binary_image = lmc_cv.threshold(gray_image, 127, 255, lmc_cv.THRESH_BINARY)
contours, hierarchy = lmc_cv.findContours(binary_image, mode=lmc_cv.RETR_TREE, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
points_number1 = 0
for i in range(len(contours)):
# 轮廓点个数
print(f"轮廓 {i + 1} 共有 {len(contours[i])} 个点: ")
points_number1 = points_number1 + len(contours[i])
# 矩(Moments)
moments = lmc_cv.moments(contours[i], binaryImage=False)
print(f"轮廓 {i + 1} 的矩: {moments}")
print(f"轮廓 {i + 1} 的质心x: {int(moments['m10'] / moments['m00'])}; 质心y: {int(moments['m01'] / moments['m00'])}")
# 轮廓面积(Area)
print(f"轮廓 {i + 1} 的面积: {lmc_cv.contourArea(contours[i], oriented=False)}")
# 轮廓周长(Perimeter)
perimeter = lmc_cv.arcLength(contours[i], closed=True)
print(f"轮廓 {i + 1} 的周长: {perimeter}")
# 轮廓近似(Contour Approximation)
approx_contour1 = lmc_cv.approxPolyDP(contours[i], epsilon=0.1 * perimeter, closed=True)
lmc_cv.drawContours(contours_image1, approx_contour1, -1, (255, 0, 0), 8)
approx_contour2 = lmc_cv.approxPolyDP(contours[i], epsilon=0.01 * perimeter, closed=True)
lmc_cv.drawContours(contours_image2, approx_contour2, -1, (0, 255, 0), 8)
# 轮廓凸包(Convex Hull)
hull_contour = lmc_cv.convexHull(contours[i], clockwise=False, returnPoints=True)
lmc_cv.drawContours(contours_image3, hull_contour, -1, (0, 0, 255), 8)
# 轮廓凸性
print(f"轮廓 {i + 1} 的凸性: {lmc_cv.isContourConvex(contours[i])}")
# 无旋转边界矩形(Bounding Rectangle)
x, y, w, h = lmc_cv.boundingRect(contours[i])
lmc_cv.rectangle(contours_image4, (x, y), (x + w, y + h), (255, 255, 0), 4)
# 有旋转边界矩形(Bounding Rectangle)
rect = lmc_cv.minAreaRect(contours[i])
box = lmc_cv.boxPoints(rect)
box = np.int0(box)
lmc_cv.drawContours(contours_image5, [box], 0, (0, 255, 255), 4)
# 最下闭合圆(Minimum Enclosing Circle)
(x, y), radius = lmc_cv.minEnclosingCircle(contours[i])
center = (int(x), int(y))
radius = int(radius)
lmc_cv.circle(contours_image6, center, radius, (255, 0, 255), 2)
# 拟合椭圆(Fitting an Ellipse)
ellipse = lmc_cv.fitEllipse(contours[i])
lmc_cv.ellipse(contours_image7, ellipse, color=(0, 255, 0), thickness=2, lineType=lmc_cv.LINE_8)
# 拟合直线(Fitting a Line)
rows, cols = contours_image8.shape[:2]
[vx, vy, x, y] = lmc_cv.fitLine(contours[i], lmc_cv.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)
lmc_cv.line(contours_image8, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)
# 显示图像
pyplot.figure('Image Display')
titles = ['Original Image', 'Contour Approximation 10%', 'Contour Approximation 1%', 'Convex Hull',
'Normal Bounding Rect', 'Rotated Bounding Rect', 'Minimum Enclosing Circle', 'Fitting an Ellipse',
'Fitting a Line']
images = [image, contours_image1, contours_image2, contours_image3, contours_image4, contours_image5,
contours_image6, contours_image7, contours_image8]
for i in range(9):
pyplot.subplot(3, 3, i + 1)
pyplot.imshow(images[i], 'gray')
pyplot.title(titles[i])
pyplot.xticks([])
pyplot.yticks([])
pyplot.show()
# 根据用户输入保存图像
if ord("q") == (lmc_cv.waitKey(0) & 0xFF):
# 销毁窗口
pyplot.close()
return

凸性缺陷

Any deviation of the object from this hull can be considered as convexity defect.

####################################################################################################
# 图像凸性缺陷(Image Convexity Defects)
def lmc_cv_image_convexity_defects():
"""
函数功能: 图像凸性缺陷(Image Convexity Defects).
物体与船体的任何偏差都可以认为是凸缺陷.
"""
# 读取图像
image = lmc_cv.imread('D:/99-Research/Python/Image/Star.png')
copy_image = image.copy()
gray_image = lmc_cv.cvtColor(image, lmc_cv.COLOR_BGR2GRAY)
# 图像凸性缺陷(Image Convexity Defects)
ret, thresh = lmc_cv.threshold(gray_image, 127, 255, lmc_cv.THRESH_BINARY)
contours, hierarchy = lmc_cv.findContours(thresh, mode=lmc_cv.RETR_CCOMP, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
for i in range(len(contours)):
cnt = contours[i]
print(f"点(50, 50)到轮廓的距离: {lmc_cv.pointPolygonTest(cnt, (50, 50), True)}")
print(f"点(100, 100)到轮廓的距离: {lmc_cv.pointPolygonTest(cnt, (100, 100), True)}")
print(f"点(150, 150)到轮廓的距离: {lmc_cv.pointPolygonTest(cnt, (150, 150), True)}")
hull = lmc_cv.convexHull(cnt, returnPoints=False)
defects = lmc_cv.convexityDefects(cnt, hull)
for j in range(defects.shape[0]):
s, e, f, d = defects[j, 0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
lmc_cv.line(copy_image, start, end, [0, 255, 0], 2)
lmc_cv.circle(copy_image, far, 5, [0, 0, 255], -1)
# 显示图像
pyplot.figure('Image Display')
titles = ['Original Image', 'Convexity Defects']
images = [image, copy_image]
for i in range(2):
pyplot.subplot(1, 2, i + 1)
pyplot.imshow(images[i], 'gray')
pyplot.title(titles[i])
pyplot.xticks([])
pyplot.yticks([])
pyplot.show()
# 根据用户输入保存图像
if ord("q") == (lmc_cv.waitKey(0) & 0xFF):
# 销毁窗口
pyplot.close()
return

形状匹配(Match Shapes)

OpenCV附带了一个函数cv.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回一个显示相似性的度量。

结果越低,匹配越好。它是根据Hu-矩值计算的。文档中解释了不同的测量方法。

####################################################################################################
# 图像轮廓形状匹配(Image Contours Match Shapes)
def lmc_cv_image_contour_match_shapes():
"""
函数功能: 图像轮廓形状匹配(Image Match Shapes).
OpenCV附带了一个函数cv.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回一个显示相似性的度量。
结果越低,匹配越好。它是根据Hu-矩不变性计算的。
Hu-矩是平移、旋转和缩放不变的7个矩。
"""
# 读取图像
image1 = lmc_cv.imread('D:/99-Research/Python/Image/Star1.png')
image2 = lmc_cv.imread('D:/99-Research/Python/Image/Star2.png')
image3 = lmc_cv.imread('D:/99-Research/Python/Image/Box1-1.png')
gray_image1 = lmc_cv.cvtColor(image1, lmc_cv.COLOR_BGR2GRAY)
gray_image2 = lmc_cv.cvtColor(image2, lmc_cv.COLOR_BGR2GRAY)
gray_image3 = lmc_cv.cvtColor(image3, lmc_cv.COLOR_BGR2GRAY)
# 图像轮廓形状匹配(Image Match Shapes)
ret1, thresh1 = lmc_cv.threshold(gray_image1, 127, 255, lmc_cv.THRESH_BINARY)
ret2, thresh2 = lmc_cv.threshold(gray_image2, 127, 255, lmc_cv.THRESH_BINARY)
ret3, thresh3 = lmc_cv.threshold(gray_image3, 127, 255, lmc_cv.THRESH_BINARY)
contours1, hierarchy1 = lmc_cv.findContours(thresh1, mode=lmc_cv.RETR_CCOMP, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
contours2, hierarchy2 = lmc_cv.findContours(thresh2, mode=lmc_cv.RETR_CCOMP, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
contours3, hierarchy3 = lmc_cv.findContours(thresh3, mode=lmc_cv.RETR_CCOMP, method=lmc_cv.CHAIN_APPROX_NONE,
offset=(0, 0))
contour1 = contours1[0]
contour2 = contours2[0]
contour3 = contours3[0]
# 形状匹配
score1 = lmc_cv.matchShapes(contour1, contour1, method=lmc_cv.CONTOURS_MATCH_I1, parameter=0.0)
score2 = lmc_cv.matchShapes(contour1, contour2, method=lmc_cv.CONTOURS_MATCH_I1, parameter=0.0)
score3 = lmc_cv.matchShapes(contour1, contour3, method=lmc_cv.CONTOURS_MATCH_I1, parameter=0.0)
# 显示图像
pyplot.figure('Image Display')
titles = ['Original Image 1 Match 1 Score: %f' % score1, 'Original Image 1 Match 2 Score: %f' % score2,
'Original Image 1 Match 3 Score: %f' % score3]
images = [image1, image2, image3]
for i in range(3):
pyplot.subplot(1, 3, i + 1)
pyplot.imshow(images[i], 'gray')
pyplot.title(titles[i])
pyplot.xticks([])
pyplot.yticks([])
pyplot.show()
# 根据用户输入保存图像
if ord("q") == (lmc_cv.waitKey(0) & 0xFF):
# 销毁窗口
pyplot.close()
return

轮廓层次结构(Contours Hierarchy)

轮廓的层次结构,即轮廓中的父子关系。

理论

当我们使用cv.findcontour()函数在图像中找到轮廓时,我们传递了一个参数,轮廓检索模式。

我们通常使用cv.RETR_LIST或者cv.RETR_TREE,它们很有效。但这到底是什么意思呢?

此外,在输出中,我们得到了三个数组,第一个是图像,第二个是轮廓,还有一个输出,我们命名为层次结构。

但我们从未在任何地方使用过这个层次结构。那么这个等级制度是什么,它的目的是什么?它与前面提到的函数参数有什么关系?

层次结构是什么?

通常我们使用cv.findcontour()函数来检测图像中的对象,对吧?有时对象在不同的位置。但在某些情况下,一些形状是在另一些形状的内部。就像嵌套数字一样。

在这种情况下,我们调用外部一个作为父节点,内部一个作为子节点。通过这种方式,图像中的轮廓彼此之间就有了某种关系。

我们可以指定一个轮廓是如何相互连接的,比如,它是其他轮廓的子轮廓,还是父轮廓,等等。这种关系的表示称为层次结构。

考虑下面的一个例子:

在这个图像中,有一些形状,我已经编号从0-5。2和2a为最外层盒子的外部和内部轮廓。

这里,轮廓0,1,2是最外面的。我们可以说,它们在hierarchy0中,或者它们在相同的hierarchylevel中。

其次是contour-2a,可以将其视为contour-2的子元素(或者反过来,contour-2是contour-2a的父元素)。所以让它在层次结构-1中。

类似地,contour3是contour2的子元素,它位于下一个层次结构中。

最后,轮廓4,5是轮廓-3a的子项,它们位于最后一层。

从我给盒子编号的方式来看,我会说contour-4是contour-3a的第一个子元素(也可以是contour-5)。

我提到这些是为了理解像相同层级,外部轮廓,子轮廓,父轮廓,第一个子轮廓等这样的术语。现在让我们进入OpenCV。

OpenCV中的层次表示

每个轮廓都有它自己的信息,关于它的等级,谁是它的子轮廓,谁是它的父轮廓等等。OpenCV将它表示为一个包含四个值的数组:[Next, Previous, First_Child, Parent]。

Next:表示在同一层次上的下一个轮廓。

例如,在我们的图中取contour-0,下一个同水平的轮廓是谁?是contour-1,把Next = 1写下来。同样,对于Contour-1,下一个是contour-2,那么Next = 2。

contour-2呢?在同一层没有下一个轮廓,简单地说,设Next = -1。contour-4呢?它与contour-5处于同一级别。所以它的下一个轮廓是contour5,所以Next = 5。

Previous:在同一轮廓上的上一个轮廓。

同上。之前的contour-1轮廓在同一水平线上为contour-0。同样,对于轮廓-2,它是轮廓-1。对于contour-0,没有previous,所以设为-1。

First_Child:表示它的第一个子轮廓。

不需要任何解释了。对于contour-2, child是contour-2a。所以它得到了轮廓-2a对应的指标值。contour-3a呢?它有两个子结点。但是我们只取第一个子轮廓,它是contour-4。因此contour-3a的First_Child = 4。

Parent:表示父轮廓的索引。

它与First_Child相反。对于轮廓-4和轮廓-5,父轮廓都是轮廓-3a。对于轮廓-3a,它是轮廓-3,以此类推。

【注意】

如果没有子轮廓或父轮廓,则将该字段作为-1。

轮廓检索模式(Contour Retrieval Mode)

1. RETR_LIST

这是四个标志中最简单的一个(从解释的角度来看)。它只是检索所有的轮廓,但没有创建任何父子关系。在这个规则下,父母和孩子是平等的,他们只是轮廓,即他们都属于同一等级。

This is the simplest of the four flags (from explanation point of view). It simply retrieves all the contours, but doesn't create any parent-child relationship. Parents and kids are equal under this rule, and they are just contours. ie they all belongs to same hierarchy level.

So here, 3rd and 4th term in hierarchy array is always -1. But obviously, Next and Previous terms will have their corresponding values. Just check it yourself and verify it.

Below is the result I got, and each row is hierarchy details of corresponding contour. For eg, first row corresponds to contour 0. Next contour is contour 1. So Next = 1. There is no previous contour, so Previous = -1. And the remaining two, as told before, it is -1.

This is the good choice to use in your code, if you are not using any hierarchy features.

2. RETR_EXTERNAL

If you use this flag, it returns only extreme outer flags. All child contours are left behind. We can say, under this law, Only the eldest in every family is taken care of. It doesn't care about other members of the family :).

So, in our image, how many extreme outer contours are there? ie at hierarchy-0 level?. Only 3, ie contours 0,1,2, right? Now try to find the contours using this flag. Here also, values given to each element is same as above. Compare it with above result.

You can use this flag if you want to extract only the outer contours. It might be useful in some cases.

3. RETR_CCOMP

This flag retrieves all the contours and arranges them to a 2-level hierarchy. ie external contours of the object (ie its boundary) are placed in hierarchy-1. And the contours of holes inside object (if any) is placed in hierarchy-2. If any object inside it, its contour is placed again in hierarchy-1 only. And its hole in hierarchy-2 and so on.

Just consider the image of a "big white zero" on a black background. Outer circle of zero belongs to first hierarchy, and inner circle of zero belongs to second hierarchy.

We can explain it with a simple image. Here I have labelled the order of contours in red color and the hierarchy they belongs to, in green color (either 1 or 2). The order is same as the order OpenCV detects contours.

So consider first contour, ie contour-0. It is hierarchy-1. It has two holes, contours 1&2, and they belong to hierarchy-2. So for contour-0, Next contour in same hierarchy level is contour-3. And there is no previous one. And its first is child is contour-1 in hierarchy-2. It has no parent, because it is in hierarchy-1. So its hierarchy array is [3,-1,1,-1]

Now take contour-1. It is in hierarchy-2. Next one in same hierarchy (under the parenthood of contour-1) is contour-2. No previous one. No child, but parent is contour-0. So array is [2,-1,-1,0].

Similarly contour-2 : It is in hierarchy-2. There is not next contour in same hierarchy under contour-0. So no Next. Previous is contour-1. No child, parent is contour-0. So array is [-1,1,-1,0].

Contour - 3 : Next in hierarchy-1 is contour-5. Previous is contour-0. Child is contour-4 and no parent. So array is [5,0,4,-1].

Contour - 4 : It is in hierarchy 2 under contour-3 and it has no sibling. So no next, no previous, no child, parent is contour-3. So array is [-1,-1,-1,3].

Remaining you can fill up. 

4. RETR_TREE

And this is the final guy, Mr.Perfect. It retrieves all the contours and creates a full family hierarchy list. It even tells, who is the grandpa, father, son, grandson and even beyond... :).

For example, I took above image, rewrite the code for cv.RETR_TREE, reorder the contours as per the result given by OpenCV and analyze it. Again, red letters give the contour number and green letters give the hierarchy order.

Take contour-0 : It is in hierarchy-0. Next contour in same hierarchy is contour-7. No previous contours. Child is contour-1. And no parent. So array is [7,-1,1,-1].

Take contour-2 : It is in hierarchy-1. No contour in same level. No previous one. Child is contour-3. Parent is contour-1. So array is [-1,-1,3,1].

And remaining, try yourself.

版权声明
本文为[机器视觉001]所创,转载请带上原文链接,感谢
https://blog.csdn.net/liubing8609/article/details/109698411

  1. 利用Python爬虫获取招聘网站职位信息
  2. Using Python crawler to obtain job information of recruitment website
  3. Several highly rated Python libraries arrow, jsonpath, psutil and tenacity are recommended
  4. Python装饰器
  5. Python实现LDAP认证
  6. Python decorator
  7. Implementing LDAP authentication with Python
  8. Vscode configures Python development environment!
  9. In Python, how dare you say you can't log module? ️
  10. 我收藏的有关Python的电子书和资料
  11. python 中 lambda的一些tips
  12. python中字典的一些tips
  13. python 用生成器生成斐波那契数列
  14. python脚本转pyc踩了个坑。。。
  15. My collection of e-books and materials about Python
  16. Some tips of lambda in Python
  17. Some tips of dictionary in Python
  18. Using Python generator to generate Fibonacci sequence
  19. The conversion of Python script to PyC stepped on a pit...
  20. Python游戏开发,pygame模块,Python实现扫雷小游戏
  21. Python game development, pyGame module, python implementation of minesweeping games
  22. Python实用工具,email模块,Python实现邮件远程控制自己电脑
  23. Python utility, email module, python realizes mail remote control of its own computer
  24. 毫无头绪的自学Python,你可能连门槛都摸不到!【最佳学习路线】
  25. Python读取二进制文件代码方法解析
  26. Python字典的实现原理
  27. Without a clue, you may not even touch the threshold【 Best learning route]
  28. Parsing method of Python reading binary file code
  29. Implementation principle of Python dictionary
  30. You must know the function of pandas to parse JSON data - JSON_ normalize()
  31. Python实用案例,私人定制,Python自动化生成爱豆专属2021日历
  32. Python practical case, private customization, python automatic generation of Adu exclusive 2021 calendar
  33. 《Python实例》震惊了,用Python这么简单实现了聊天系统的脏话,广告检测
  34. "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system in Python
  35. Convolutional neural network processing sequence for Python deep learning
  36. Python data structure and algorithm (1) -- enum type enum
  37. 超全大厂算法岗百问百答(推荐系统/机器学习/深度学习/C++/Spark/python)
  38. 【Python进阶】你真的明白NumPy中的ndarray吗?
  39. All questions and answers for algorithm posts of super large factories (recommended system / machine learning / deep learning / C + + / spark / Python)
  40. [advanced Python] do you really understand ndarray in numpy?
  41. 【Python进阶】Python进阶专栏栏主自述:不忘初心,砥砺前行
  42. [advanced Python] Python advanced column main readme: never forget the original intention and forge ahead
  43. python垃圾回收和缓存管理
  44. java调用Python程序
  45. java调用Python程序
  46. Python常用函数有哪些?Python基础入门课程
  47. Python garbage collection and cache management
  48. Java calling Python program
  49. Java calling Python program
  50. What functions are commonly used in Python? Introduction to Python Basics
  51. Python basic knowledge
  52. Anaconda5.2 安装 Python 库(MySQLdb)的方法
  53. Python实现对脑电数据情绪分析
  54. Anaconda 5.2 method of installing Python Library (mysqldb)
  55. Python implements emotion analysis of EEG data
  56. Master some advanced usage of Python in 30 seconds, which makes others envy it
  57. python爬取百度图片并对图片做一系列处理
  58. Python crawls Baidu pictures and does a series of processing on them
  59. python链接mysql数据库
  60. Python link MySQL database