逼近多边形是轮廓的高度近似。有时候仅需要一个多边形的凸包来简化它。凸包与轮廓之间的部分,称之为凸缺陷。介绍如何判断某点与轮廓的位置关系。

凸包

凸包跟逼近多边形很像,只不过它是物体最外层的“凸”多边形。凸包指的是完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。

凸缺陷指边缘与凸包之间部分。能够用来处理手势识别等问题。

语法:

hull = cv2.convexHull(points[,clockwose[,returnPoints]])
  • points表示轮廓
  • clockwose为True时,凸包角点将按顺时针方向排列;该值为False时,则以逆时针方向排列凸包角点
  • returnPoints为True时(默认值),返回凸包角点的x,y坐标;当为false时,返回轮廓中凸包角点的索引

例:观察参数returnPoints对返回值的影响。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\A5.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理

# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

hull = cv2.convexHull(contours[0])  #返回坐标值
print('returnPoints为默认值True时返回坐标hull:',hull)
hull2 = cv2.convexHull(contours[0],returnPoints=False)  #返回索引
print('returnPoints为False时返回坐标hull2:',hull2)

运行结果:

returnPoints为默认值True时返回坐标hull: [[[483 227]]

[[431 214]]

[[324 187]]

[[246 152]]

[[172 114]]

[[171 113]]

[[291 89]]

[[293 89]]

[[297 91]]

[[345 120]]

[[353 125]]

[[356 127]]

[[411 166]]

[[482 226]]]
returnPoints为False时返回坐标hull2: [[274]
[248]
[194]
[125]
[ 49]
[ 48]
[ 0]
[497]
[493]
[455]
[449]
[447]
[371]
[273]]

例:使用函数cv2.convexHull()获取轮廓的凸包。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\hand.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理

# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

# =====寻找凸包,得到角点=====
hull = cv2.convexHull(contours[0])  #返回坐标值

# =====绘制凸包=====
cv2.polylines(img, [hull], True, (0,255,0), 2)

# =====显示凸包=====
cv2.imshow("RESULT",img)
cv2.waitKey()

凸缺陷

语法:

conDef = cv2.convexityDefects(contour, convexhull)
  • 返回值conDef是凸缺陷点集,是一个数组
  • contour是轮廓
  • convexhull是凸包
  •  获取参数convexhull的值时,使用了cv2.convexHull(),此时参数必须returnPoints=False 
import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\hand.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理

# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

# =====寻找凸包,得到角点=====
cnt = contours[0]
hull = cv2.convexHull(contours[0],returnPoints=False)  #返回坐标值
defect = cv2.convexityDefects(cnt,hull)

# =====构造凸缺陷=====
for i in range(defect.shape[0]):
    s,e,f,d = defect[i,0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(img,start,end,[0,0,255],2)
    cv2.circle(img,far,5,[255,0,0],-1)

# =====显示=====
cv2.imshow("RESULT",img)
cv2.waitKey()

计算凸缺陷

几何学测试

测试轮廓是否为凸形

语法:

retval = cv2.isContourConvex(contours)
  • 返回值retval是布尔类型,该值为True时,表示轮廓为凸形,否则,为不凸
  • contours是被判断的轮廓

点到轮廓的距离

语法:

retval = cv2.pointPolygonTest(contour, pt, measureDist)
  • contour为轮廓
  • pt是待判断的点
  • measureDist为布尔类型,表示判断距离的方式
    • 为True时,表示计算点到轮廓的距离。如果点在轮廓的外部,返回值负数;如果点在轮廓上,则返回0;如果点在轮廓的内部,返回值为正数;
    • 为False时,不计算距离。如果点在轮廓的外部,返回值为-1;如果点在轮廓上,则返回0;如果点在轮廓的内部,返回值为1;

例:使用函数cv2.pointPolygonTest()计算点到轮廓的最短距离。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\A5.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理
image = gray
# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

# =====获取凸包=======
hull = cv2.convexHull(contours[0])
image = cv2.cvtColor(image,cv2.COLOR_GRAY2BGR)
cv2.polylines(image,[hull],True,(0,255,0),2)

# =====内部点A到轮廓的距离=====
distA = cv2.pointPolygonTest(hull,(300,150),True)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'A',(300,150),font,1,(0,255,0),3)
print("distA=",distA)

# =====外部点B到轮廓的距离=====
distB = cv2.pointPolygonTest(hull,(300,250),True)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'B',(300,250),font,1,(0,255,0),3)
print("distB=",distB)

# =====正好处于轮廓上的点C到轮廓的距离=====
distC = cv2.pointPolygonTest(hull,(174,115),True)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'C',(174,115),font,1,(0,255,0),3)
print("distC=",distC)

# =====显示=====
cv2.imshow("RESULT",image)
cv2.waitKey()

结果:

distA= 23.9318707374807
distB= -67.4166151627327
distC= 0.02404235184171725(近似在轮廓上)

结果

例2:使用函数cv2.pointPolygonTest()判断点与轮廓的关系。

思路:根据上面的距离计算,将measureDist改为False即可。

import cv2
img = cv2.imread(r'E:\Blog\OpenCV\Process\A5.png')
cv2.imshow("IMG",img)
# =====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理
image = gray
# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

# =====获取凸包=======
hull = cv2.convexHull(contours[0])
image = cv2.cvtColor(image,cv2.COLOR_GRAY2BGR)
cv2.polylines(image,[hull],True,(0,255,0),2)

# =====内部点A到轮廓的距离=====
distA = cv2.pointPolygonTest(hull,(300,150),False)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'A',(300,150),font,1,(0,255,0),3)
print("distA=",distA)

# =====外部点B到轮廓的距离=====
distB = cv2.pointPolygonTest(hull,(300,250),False)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'B',(300,250),font,1,(0,255,0),3)
print("distB=",distB)

# =====正好处于轮廓上的点C到轮廓的距离=====
distC = cv2.pointPolygonTest(hull,(174,115),False)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(image,'C',(174,115),font,1,(0,255,0),3)
print("distC=",distC)

# =====显示=====
cv2.imshow("RESULT",image)
cv2.waitKey()

运行结果:

distA= 1.0
distB= -1.0
distC= 1.0(实际上并没有在轮廓上,只是离的很近,和上面距离结果是一致的)

结果


博主个人公众号
版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/OpenCV38.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

最后修改:2022 年 05 月 27 日
如果觉得我的文章对你有用,请随意赞赏