学习目标
- 凸缺陷的查找
- 求某一点到一个多边形的最短距离
- 不同形状的匹配
凸缺陷
前面我们已经学习了轮廓的凸包,对象上的任何凹陷都被成为凸缺陷。OpenCV 中有一个函数cv2.convexityDefect() 可以帮助我们找到凸缺。函数使用如下:
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
注意:如果查查找凸缺陷,在使用函数cv2.convexHull 找凸包时,参数returnPoints 一定要是False。
它会返回一个数组,其中每一行包含的值是[起点,终点,最远的点,到最远点的近似距离]。我们可以在一张图上显示它。我们将起点和终点用一条绿线连接,在最远点画一个圆圈,要记住的是返回结果的前三个值是轮廓点的索引。所以我们还要到轮廓点中去找它们。
import cv2
import numpy as np
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()
结果如下:
点多边形测试
点多边形测试(Point Polygon Test)是求解图像中的一个点到一个对象轮廓的最短距离。如果点在轮廓的外面,返回值为负;如果在轮廓上,返回值为0;如果在廓内,返回值为正。
下面我们以点(50,50)为例:
dist = cv2.pointPolygonTest(cnt,(50,50),True)
此函数的第三个参数是measureDist。如果设置为True,就会计算最短距离。如果是False,只会判断这个点与轮廓之间的位置关系,返回值为(+1,-1,0)
注意:如果你不知道具体距离,建议你将第三个参数设置为False,这样速度会度会提高2到3倍。
形状匹配
函数cv2.matchShape() 可以帮我们比较两个形状或轮廓的相似度。如果返回值越小,匹配度越好。它是根据Hu 矩来计算的。文档中对不同的方法都有解释。我们试着将下面的图形进行比较:
import cv2
import numpy as npimg1 = cv2.imread('star.jpg',0)
img2 = cv2.imread('star2.jpg',0)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
看到了吧,即使发生了旋转对匹配的结果影响也不是非常大。
示例
1.创建一个小程序,可以将图片上的点绘制成不同的颜色,颜色是根据这个点到轮廓的距离来决定的。要使用的函数:cv2.pointPolygonTest()。
以下是一个示例代码,演示如何使用cv2.pointPolygonTest()
函数根据点到轮廓的距离来确定颜色:
import cv2
import numpy as np# 加载图像
image = cv2.imread('image.jpg')# 将图像转为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 二值化图像
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 创建一个空白图像
result = np.zeros_like(image)# 绘制轮廓
cv2.drawContours(result, contours, -1, (255, 255, 255), 2)# 遍历图像上的每个点
for y in range(result.shape[0]):for x in range(result.shape[1]):# 计算点到轮廓的距离distance = cv2.pointPolygonTest(contours[0], (x, y), True)# 根据距离设置颜色if distance > 0:result[y, x] = (0, 0, 255) # 蓝色elif distance < 0:result[y, x] = (0, 255, 0) # 绿色else:result[y, x] = (255, 0, 0) # 红色# 显示图像
cv2.imshow("Distance to Contour", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
在上述代码中,首先加载图像,并将图像转换为灰度图像。然后使用cv2.threshold()
函数对灰度图像进行二值化处理,得到二值图像binary
。接下来使用cv2.findContours()
函数查找轮廓,并将轮廓保存在contours
中。然后创建一个空白图像result
,并使用cv2.drawContours()
函数绘制轮廓。接着使用两层循环遍历图像上的每个点,对每个点使用cv2.pointPolygonTest()
函数计算该点到轮廓的距离,根据距离设置点的颜色。
最后,显示图像。点到轮廓的距离为正数表示点在轮廓的外部,为负数表示点在轮廓的内部,为零表示点在轮廓上。根据距离的正负,我们将点的颜色设置为蓝色、绿色或红色。请确保将代码中的image.jpg
替换为你要处理的图像文件的路径。
2. 使用函数cv2.matchShapes() 匹配带有字母或者数字的图片。
以下是一个使用cv2.matchShapes()
函数匹配带有字母或数字的图片的示例代码:
import cv2
import numpy as np# 加载模板图像
template = cv2.imread('template.jpg', 0)# 加载目标图像
target = cv2.imread('target.jpg', 0)# 二值化模板图像和目标图像
_, template_binary = cv2.threshold(template, 127, 255, cv2.THRESH_BINARY)
_, target_binary = cv2.threshold(target, 127, 255, cv2.THRESH_BINARY)# 查找模板图像和目标图像的轮廓
template_contours, _ = cv2.findContours(template_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
target_contours, _ = cv2.findContours(target_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 计算模板图像和目标图像的Hu矩
template_hu_moments = cv2.HuMoments(cv2.moments(template_contours[0])).flatten()
target_hu_moments = cv2.HuMoments(cv2.moments(target_contours[0])).flatten()# 计算模板图像和目标图像的形状匹配分数
match_score = cv2.matchShapes(template_contours[0], target_contours[0], cv2.CONTOURS_MATCH_I1, 0)# 输出结果
print("Hu Moments of Template Image: ", template_hu_moments)
print("Hu Moments of Target Image: ", target_hu_moments)
print("Shape Matching Score: ", match_score)
在上述代码中,首先加载模板图像和目标图像,并将它们转换为灰度图像。然后使用cv2.threshold()
函数对模板图像和目标图像进行二值化处理,得到二值图像template_binary
和target_binary
。
接下来使用cv2.findContours()
函数查找模板图像和目标图像的轮廓,并将轮廓保存在template_contours
和target_contours
中。
然后计算模板图像和目标图像的Hu矩,使用cv2.moments()
函数计算图像的矩,再使用cv2.HuMoments()
函数计算Hu矩,并将其展平为一维数组。
最后使用cv2.matchShapes()
函数计算模板图像和目标图像的形状匹配分数。cv2.matchShapes()
函数的第一个参数是模板图像的轮廓,第二个参数是目标图像的轮廓,第三个参数是形状匹配方法,第四个参数是匹配时的数值参数。请确保将代码中的template.jpg
和target.jpg
替换为你要处理的模板图像和目标图像的路径。