新视界-OpenCV教程系列文章
新视界-OpenCV教程(1)-入门介绍
OpenCV 中的图形用户界面特征系列
新视界-OpenCV教程(2)-图片入门
新视界-OpenCV教程(3)-视频入门
新视界-OpenCV教程(4)- 绘图功能
新视界-OpenCV教程(5)- 鼠标的画笔功能
新视界-OpenCV教程(6)- 作为调色板的轨迹栏
核心操作系列
新视界-OpenCV教程(7)- 对图片的基本操作
新视界-OpenCV教程(8)- 图像运算
新视界-OpenCV教程(9)- 性能测量和改进技术
OpenCV 中的图像处理系列
新视界-OpenCV教程(10)- 改变颜色空间
新视界-OpenCV教程(11)- 图像阈值
新视界-OpenCV教程(12)- 图像的几何变换
新视界-OpenCV教程(13)- 平滑图像
新视界-OpenCV教程(14)- 形态变换
新视界-OpenCV教程(15)- 图像梯度
新视界-OpenCV教程(16)- Canny 边缘检测
新视界-OpenCV教程(17)- 图像金字塔
新视界-OpenCV教程(18)- 轮廓:开端
新视界-OpenCV教程(19)- 轮廓:特征
新视界-OpenCV教程(20)- 轮廓:性质
本文目标
轮廓系列会分为5篇文章一一详细讲解。这是第四篇。
在本篇文章中,我们将学习凸性缺陷及其查找方法,如何求从一点到多边形的最短距离 和 如何匹配不同的形状。
凸性缺陷
我们在先前文章中讨论过轮廓的凸包。该凸包中目标物体的任何偏差都可被视为凸性缺陷。
OpenCV 为我们提供了一个现成的函数,cv2.convexityDefects() 来解决这个问题。一个基本的函数调用如下所示:
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
记住,我们在寻找凸包时必须保证 returnPoints = False,才能顺利找到凸性缺陷。
我们上面的函数会返回一个数组作为结果,数组中每行包含这些:[起点、终点、最远点、到最远点的近似距离],这些我们可以用图像来表示:我们先画一条连接起点和终点的线,然后在最远处画一个圆。记住,返回的前三个值是cnt 的索引,所以我们必须从cnt 中得到这些值。代码如下:
import cv2
import numpy as np
# 导入图片,转换成灰度,得到阈值,轮廓,层级和cnt
img = cv2.imread('star.jpg')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv2.findContours(thresh,2,1)
cnt = contours[0]
# 使用凸性缺陷相关函数
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
# 使用循环画出范围内的所有线和圆圈
for i in range(defects.shape[0]):
s,e,f,d = defects[i,0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv2.line(img,start,end,[0,255,0],2)
cv2.circle(img,far,5,[0,0,255],-1)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
我们可以得到结果如下:
点多边形测试
在这个部分中,我们的函数用来查找图像中某一点到轮廓线的最短距离。它返回的距离如果是负的,那么点在轮廓外;如果返回的值为正的,那么点在轮廓内;如果值为零,那么点在轮廓上。
例如,我们可以检查点(50,50),代码如下:
dist = cv2.pointPolygonTest(cnt,(50,50),True)
在上述函数中,第三个参数是measureDist。如果它为True,函数就会找到有符号的距离;如果为False,它就会查找该点是在等高线内还是外还是在等高线上(分别返回+1、-1、0)。
注意,如果目的不是求距离,我们则需要确保第三个参数是假的。因为,求距离是一个耗时的过程。如果让该参数为False,省去求距离的过程,我们会得到2-3 倍的加速。
匹配形状
OpenCV 还附带了一个函数cv2.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回一个显示相似性的度量。度量结果越低,匹配越好,它是根据hu 矩值计算的。
import cv2
import numpy as np
# 导入两张图
img1 = cv2.imread('star.jpg',0)
img2 = cv2.imread('star2.jpg',0)
# 得到阈值,轮廓,层级和两个cnt
ret, thresh = cv2.threshold(img1, 127, 255,0)
ret, thresh2 = cv2.threshold(img2, 127, 255,0)
contours,hierarchy = cv2.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv2.findContours(thresh2,2,1)
cnt2 = contours[0]
# 利用匹配函数得到相似度
ret = cv2.matchShapes(cnt1,cnt2,1,0.0)
print ret
我尝试将特定的形状与下面给出的形状进行匹配:
我得到了以下结果:
- 将图像A与自身匹配,精度 = 0.0,完全相似
- 图像A与图像B匹配,精度 = 0.001946,相似度非常高
- 匹配图像A与图像C,精度 = 0.326911,相似度低
从上面的例子我们也可以看出,即使图像旋转对我们的比较也没有多大影响。
注:hu 矩是无论平移、旋转还是尺度变化都对其无影响的七个矩,第七项是偏不变量 skew-invariant,这些值我们可以使用cv2.HuMoments() 函数找到。
在下一篇文章中,将着重讲的是新视界-OpenCV教程(22)- 轮廓:层级。
如果你觉得我的文章有用,顺手点个赞,关注下我的专栏或则留下你的评论吧!