import math
import cv2
hand1 = cv2.imread(‘hand1.png’) #布
hand2 = cv2.imread(‘hand2.png’) #剪刀
hand3 = cv2.imread(‘hand4.png’) #石头
hand1 = cv2.resize(hand1,(500,500))
hand2 = cv2.resize(hand2,(500,500))
hand3 = cv2.resize(hand3,(500,500))
OH1 = hand1.copy()
OH2 = hand2.copy()
OH3 = hand3.copy()
hand1 = cv2.cvtColor(hand1,cv2.COLOR_BGR2GRAY)
hand2 = cv2.cvtColor(hand2,cv2.COLOR_BGR2GRAY)
hand3 = cv2.cvtColor(hand3,cv2.COLOR_BGR2GRAY)
ret,hand1 = cv2.threshold(hand1,127,255,cv2.THRESH_BINARY_INV)
ret,hand2 = cv2.threshold(hand2,60,255,cv2.THRESH_BINARY_INV)
ret,hand3 = cv2.threshold(hand3,127,255,cv2.THRESH_BINARY_INV)
contours1,h1 = cv2.findContours(hand1,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
contours2,h2 = cv2.findContours(hand2,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
contours3,h3 = cv2.findContours(hand3,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#逼近多边形是轮廓的高度近似,但有时候我们希望使用一个多边形的凸包
#来简化他,凸包和逼近多边形很像。任何俩点的直线都在凸包内部
#任意连续三个点内部角都小于180度
#opencv提供函数cv2.convexHull()用于获取轮廓的凸包
#语法格式是hull = cv2.convexHull(points[,clockwise[,returnPoints]])
#points表示轮廓
#clockwise表示布尔型号 Ture时凸包角点顺时针 False时凸包角点是逆时针
#returnPoints 表示布尔型号,Ture 角点坐标值 False 凸包角点的索引
hull1 = cv2.convexHull(contours1[0],returnPoints = False)
hull2 = cv2.convexHull(contours2[0],returnPoints = False)
hull3 = cv2.convexHull(contours3[0],returnPoints = False)
#凸缺陷
#凸缺陷与轮廓之间的部分称为凸缺陷
#凸缺陷可用来处理手势识别问题
#通常情况下四个特征值来表示凸缺陷
#1 起点
#2 重点
#3 轮廓上距离凸包最远的点
#4 最远点到凸包的近似距离
#opencv提供了cv2.convexityDefects来获取凸缺陷
#convexityDefects = cv2.convexityDefects(contours,convexhull)
#返回值为凸缺陷点集 每行包括[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离]
#值得注意的是如果要寻找凸缺陷那么用convexHull寻找凸包时候returnPints = False才行
defects1 = cv2.convexityDefects(contours1[0],hull1)
n1 = 0
for i in range(defects1.shape[0]):
s,e,f,d = defects1[i,0]
start = tuple(contours1[0][s][0])
end = tuple(contours1[0][e][0])
far = tuple(contours1[0][f][0])
a = math.sqrt((end[0]-start[0])**2+(end[1] - start[1])2)
b = math.sqrt((far[0]-start[0])2+(far[1] - start[1])2)
c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
#计算手指之间的角度
angle = math.acos((b2 + c2-a2)/(2bc))57
#绘制手指间的凸包最远点 角度介于20-90的认为不同手指构成的凸缺陷
if angle<=90 and d >=20:
n1 = n1 + 1
cv2.circle(OH1, far, 5, [255, 0, 0], -1)
cv2.line(OH1,start,end,[0,0,255],2)
n2 = 0
defects2 = cv2.convexityDefects(contours2[0],hull2)
for i in range(defects2.shape[0]):
s,e,f,d = defects2[i,0]
start = tuple(contours2[0][s][0])
end = tuple(contours2[0][e][0])
far = tuple(contours2[0][f][0])
a = math.sqrt((end[0]-start[0])**2+(end[1] - start[1])2)
b = math.sqrt((far[0]-start[0])2+(far[1] - start[1])2)
c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
#计算手指之间的角度
angle = math.acos((b2 + c2-a2)/(2bc))57
#绘制手指间的凸包最远点 角度介于20-90的认为不同手指构成的凸缺陷
if angle<=90 and d >=20:
n2 = n2 + 1
cv2.circle(OH2,far,5,[255,0,0],-1)
cv2.line(OH2,start,end,[0,0,255],2)
defects3 = cv2.convexityDefects(contours3[0],hull3)
n3 = 0
for i in range(defects3.shape[0]):
s,e,f,d = defects3[i,0]
start = tuple(contours3[0][s][0])
end = tuple(contours3[0][e][0])
far = tuple(contours3[0][f][0])
a = math.sqrt((end[0]-start[0])**2+(end[1] - start[1])2)
b = math.sqrt((far[0]-start[0])2+(far[1] - start[1])2)
c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
#计算手指之间的角度
angle = math.acos((b2 + c2-a2)/(2bc))*57
#绘制手指间的凸包最远点 角度介于20-90的认为不同手指构成的凸缺陷
if angle<=90 and d >=20:
n3 = n3 + 1
cv2.circle(OH3,far,5,[255,0,0],-1)
cv2.line(OH3,start,end,[0,0,255],2)
#凸缺陷有很重要的实践意义,利用凸缺陷检测各种物体是否存在缺陷
#我们在此找到了很多凸缺陷但是有很多凸缺陷是无用的
#比如用缺陷及识别手势 4个缺陷=》5 3个缺陷=》4 2个缺陷=》3 1缺陷=》2
#问题是0个缺陷时可能是手势0或手势1
#此时使用其他方式
#1 若凸缺陷面积相对较小,则处理为噪声
#2 凸缺陷最远点与起点、终点构成的角度大于90,则处理为噪声。因为人指缝的角度通常小于90.
#3 凸缺陷最远点到凸包的近似距离小 则将其处理为噪声
#4 轮廓面积/凸包面积(轮廓面积 + 凸缺陷面积) > 0.9 两者基本一致所以是石头 轮廓面积/凸包面积(轮廓面积 + 凸缺陷面积) <= 0.9 凸包比较大所以可能是1
#检测下是不是石头
hull1T = cv2.convexHull(contours1[0])
hull2T = cv2.convexHull(contours2[0])
hull3T = cv2.convexHull(contours3[0])
areacnt1 = cv2.contourArea(contours1[0]) #轮廓面积
areahull1 = cv2.contourArea(hull1T)
arearatio1 = areacnt1/areahull1
areacnt2 = cv2.contourArea(contours2[0]) #轮廓面积
areahull2 = cv2.contourArea(hull2T)
arearatio2 = areacnt2/areahull2
areacnt3 = cv2.contourArea(contours3[0]) #轮廓面积
areahull3 = cv2.contourArea(hull3T)
arearatio3 = areacnt3/areahull3
print(‘图片一是布面积为’+str(arearatio1)+‘图片二是剪刀面积为’+str(arearatio2)+‘图片三是石头面积为’+str(arearatio3))
if arearatio1 >0.9:
cv2.putText(OH1,‘Rock’,(0,80),cv2.FONT_HERSHEY_SIMPLEX,3,(0,255,0),2)
if arearatio2 >0.9:
cv2.putText(OH2,‘Rock’,(0,80),cv2.FONT_HERSHEY_SIMPLEX,3,(0,255,0),2)
if arearatio3 >0.9:
cv2.putText(OH3,‘Rock’,(0,80),cv2.FONT_HERSHEY_SIMPLEX,3,(0,255,0),2)
#识别是布还是剪刀
if n1 == 0:
cv2.putText(OH1, ‘Rock’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n1 == 1:
cv2.putText(OH1, ‘scissors’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n1 == 4:
cv2.putText(OH1, ‘cloth’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
if n2 == 0:
cv2.putText(OH2, ‘Rock’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n2 == 1:
cv2.putText(OH2, ‘scissors’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n2 == 4:
cv2.putText(OH2, ‘cloth’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
if n3 == 0:
cv2.putText(OH3, ‘Rock’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n3 == 1:
cv2.putText(OH3, ‘scissors’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
elif n3 == 4:
cv2.putText(OH3, ‘cloth’, (0, 80), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 0), 2)
cv2.imshow(‘hand1’,OH1)
cv2.imshow(‘hand2’,OH2)
cv2.imshow(‘hand3’,OH3)
cv2.waitKey(0)