opencv基础52-图像轮廓学习05-凸包获取-cv2.convexHull()

逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。
凸包跟逼近多边形很像,只不过它是物体最外层的“凸”多边形。凸包指的是完全包含原有轮 廓,并且仅由轮廓上的点所构成的多边形。凸包的每一处都是凸的,即在凸包内连接任意两点 的直线都在凸包的内部。在凸包内,任意连续三个点的内角小于 180°。

例如,在图 12-25 中,最外层的多边形为机械手的凸包,在机械手边缘与凸包之间的部分被称为凸缺陷(Convexity Defect),凸缺陷能够用来处理手势识别等问题。

在这里插入图片描述

获取凸包

OpenCV 提供函数 cv2.convexHull()用于获取轮廓的凸包。该函数的语法格式为:

hull = cv2.convexHull( points[, clockwise[, returnPoints]] )

式中的返回值 hull 为凸包角点。
式中的参数如下:

  • points:轮廓。
  • clockwise:布尔型值。该值为 True 时,凸包角点将按顺时针方向排列;该值为 False 时,则以逆时针方向排列凸包角点。
  • returnPoints:布尔型值。默认值是 True,函数返回凸包角点的 x/y 轴坐标;当为 False时,函数返回轮廓中凸包角点的索引。

示例:观察函数 cv2.convexHull()内参数 returnPoints 的使用情况

代码如下:

import cv2
o = cv2.imread('contours.bmp')
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
hull = cv2.convexHull(contours[0]) # 返回坐标值
print("returnPoints 为默认值 True 时返回值 hull 的值:\n",hull)
hull2 = cv2.convexHull(contours[0], returnPoints=False) # 返回索引值
print("returnPoints 为 False 时返回值 hull 的值:\n",hull2)

运行结果:

returnPoints 为默认值 True 时返回值 hull 的值:
 [[[195 270]]

 [[195 383]]

 [[ 79 383]]

 [[ 79 270]]]
returnPoints 为 False 时返回值 hull 的值:
 [[3]
 [2]
 [1]
 [0]]

从程序运行结果可以看出,函数 cv2.convexHull()的可选参数returnPoints:

  • 为默认值 True 时,函数返回凸包角点的 x/y 轴坐标,本例中返回了 4 个轮廓的坐标值。
  • 为 False 时,函数返回轮廓中凸包角点的索引,本例中返回了 4 个轮廓的索引值。

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

代码如下:

import cv2
# --------------读取并绘制原始图像------------------
o = cv2.imread('hand.bmp')
cv2.imshow("original",o)
# --------------提取轮廓------------------
gray = cv2.cvtColor(o,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)
# --------------寻找凸包,得到凸包的角点------------------
hull = cv2.convexHull(contours[0])
# --------------绘制凸包------------------
cv2.polylines(o, [hull], True, (0, 255, 0), 2)
# --------------显示凸包------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述

凸缺陷

凸包与轮廓之间的部分,称为凸缺陷。在 OpenCV 中使用函数 cv2.convexityDefects()获取凸缺陷。其语法格式如下:

convexityDefects = cv2.convexityDefects( contour, convexhull )

式中的返回值 convexityDefects 为凸缺陷点集。它是一个数组,每一行包含的值是[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]。

需要注意的是,返回结果中[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]的前三个值是轮廓点的索引,所以需要到轮廓点中去找它们。
式中的参数如下:

  • contour 是轮廓。
  • convexhull 是凸包。

需要注意的是,用 cv2.convexityDefects()计算凸缺陷时,要使用凸包作为参数。在查找该凸包时,所使用函数 cv2.convexHull()的参数 returnPoints 的值必须是 False。
为了让大家更直观地观察凸缺陷点集,我们尝试将凸缺陷点集在一幅图内显示出来。实现方式为,将起点和终点用一条线连接,在最远点画一个圆圈。下面我们通过一个例子来展示上述操作。

示例2:使用函数 cv2.convexityDefects()计算凸缺陷。

代码如下:

import cv2
#----------------原图--------------------------
img = cv2.imread('hand.bmp')
cv2.imshow('original',img)
#----------------构造轮廓--------------------------
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255,0)
contours, hierarchy = cv2.findContours(binary,
 cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
#----------------凸包--------------------------
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
print("defects=\n",defects)
#----------------构造凸缺陷--------------------------
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,0,255],2)
 cv2.circle(img,far,5,[255,0,0],-1)
#----------------显示结果,释放图像--------------------------
cv2.imshow('result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果:

defects=
 [[[    0   102    51 21878]]

 [[  103   184   150 13876]]

 [[  185   233   220  4168]]

 [[  233   238   235   256]]

 [[  238   240   239   247]]

 [[  240   294   255  2715]]

 [[  294   302   295   281]]

 [[  302   304   303   217]]

 [[  305   311   306   114]]

 [[  311   385   342 13666]]

 [[  385   389   386   395]]

 [[  389   489   435 20327]]]

在这里插入图片描述

判断轮廓是否是凸形的

在 OpenCV 中,可以用函数 cv2.isContourConvex()来判断轮廓是否是凸形的,其语法格式为:

retval = cv2.isContourConvex( contour )

式中:

  • 返回值 retval 是布尔型值。该值为 True 时,表示轮廓为凸形的;否则,不是凸形的。
  • 参数 contour 为要判断的轮廓。

示例:使用函数 cv2.isContourConvex()来判断轮廓是否是凸形的。

代码如下:

import cv2
o = cv2.imread('hand.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,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)
#--------------凸包----------------------
image1=o.copy()
hull = cv2.convexHull(contours[0])
cv2.polylines(image1, [hull], True, (0, 255, 0), 2)
print("使用函数 cv2.convexHull()构造的多边形是否是凸形的:",
cv2.isContourConvex(hull))
cv2.imshow("result1",image1)
#------------逼近多边形--------------------
image2=o.copy()
epsilon = 0.01*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
image2=cv2.drawContours(image2,[approx],0,(0,0,255),2)
print("使用函数 cv2.approxPolyDP()构造的多边形是否是凸形的:",
 cv2.isContourConvex(approx))
cv2.imshow("result2",image2)
#------------释放窗口--------------------
cv2.waitKey()
cv2.destroyAllWindows()

运行结果:

使用函数 cv2.convexHull()构造的多边形是否是凸形的: True
使用函数 cv2.approxPolyDP()构造的多边形是否是凸形的: False

在这里插入图片描述

从以上运行结果可以看出:

  • 使用函数 cv2.convexHull()构造凸包后,对绘制的凸包使用函数 cv2.isContourConvex()判断,返回值为 True,说明该轮廓是凸形的。
  • 使用函数 cv2.approxPolyDP()构造逼近多边形后,对绘制的逼近多边形使用函数cv2.isContourConvex()判断,返回值为 False,说明该轮廓(多边形)不是凸形的。

实验原图:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hai411741962/article/details/132192433
今日推荐