基于OpenCV与Python的简易手势识别实现
本文介绍使用OpenCV和Python3.5实现简易手势识别的方法。通过摄像头捕获视频,利用椭圆肤色检测模型提取皮肤区域,结合高斯滤波、膨胀与腐蚀去噪,以及Canny边缘检测优化图像。最终采用cv2.matchShapes进行轮廓匹配,并引入矩形框架面积判断以提高识别准确率,实现剪刀、石头、布三种手势的分类。
本文旨在通过摄像头输入实现剪刀、石头、布三种手势的检测,具体实现步骤如下:
- 首先需选择合适的颜色空间并设定阈值,以准确提取图像中的皮肤区域。
- 随后利用滤波、腐蚀和膨胀等图像处理技术去除噪声,提升图像质量。
- 接着进行边缘检测,以清晰勾勒出手势轮廓。
- 最后采用合适的方法对提取的轮廓进行分类识别。
在OpenCV中,可以通过调用相关接口来捕获视频流。
具体实现时,调用OpenCV库中的cv2.VideoCapture()函数来初始化摄像头设备。
def video_capture():
cap = cv2.VideoCapture(0)
while True:
# capture frame-by-frame
ret, frame = cap.read()
# our operation on the frame come here
# gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 可选择灰度化
# display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'): # 按q键退出
break
# when everything done , release the capture
cap.release()
cv2.destroyAllWindows()
初始运行效果如下图所示:

肤色识别环节采用椭圆肤色检测模型。
相关原理可参考以下博文:
https://www.jb51.net/article/202594.htm
具体代码实现如下:
def ellipse_detect(img):
# 椭圆肤色检测模型
skinCrCbHist = np.zeros((256, 256), dtype=np.uint8)
cv2.ellipse(skinCrCbHist, (113, 155), (23, 15), 43, 0, 360, (255, 255, 255), -1)
YCRCB = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
(y, cr, cb) = cv2.split(YCRCB)
skin = np.zeros(cr.shape, dtype=np.uint8)
(x, y) = cr.shape
for i in range(0, x):
for j in range(0, y):
CR = YCRCB[i, j, 1]
CB = YCRCB[i, j, 2]
if skinCrCbHist[CR, CB] > 0:
skin[i, j] = 255
dst = cv2.bitwise_and(img, img, mask=skin)
return dst
从效果可见,该方法会将所有与肤色相近的物体(包括桌子等背景)一并提取出来,因此识别时需尽量保持环境无干扰。

去噪处理主要采用滤波、腐蚀和膨胀技术。
相关技术细节可参考以下博文:
https://www.jb51.net/article/202599.htm
具体采用的方法为高斯滤波cv2.GaussianBlur()配合膨胀操作cv2.dilate(),代码如下:
# 膨胀
def dilate_demo(image):
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 定义结构元素的形状和大小
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 膨胀操作
dst = cv2.dilate(binary, kernel)
return dst
# 腐蚀
def erode_demo(image):
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 定义结构元素的形状和大小
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15))
# 腐蚀操作
dst = cv2.erode(binary, kernel)
return dst
# 滤波
def img_blur(image):
# 腐蚀操作
# img_erode = erode_demo(image)
# 膨胀操作
img_dilate = dilate_demo(image)
# 均值滤波
# blur = cv2.blur(image, (5, 5))
# 高斯滤波
blur = cv2.GaussianBlur(img_dilate, (3, 3), 0)
return blur
随后进行Canny边缘检测。
可参考OpenCV中文教程获取更多信息。
https://www.kancloud.cn/aollo/aolloopencv/271603
边缘检测代码如下:
# Canny边缘检测v
def canny_detect(image):
edges = cv2.Canny(image, 50, 200)
return edges
识别环节采用轮廓匹配算法。
由于Tensorflow框架搭建复杂且易出错,此处改用轮廓匹配方法cv2.matchShapes(),具体方案如下:
- 首先划分出一个手势识别区域,以排除周围环境的干扰,并简化图像处理流程。
- 在寻找轮廓时,采用cv2.boundingRect()方法获取最大轮廓,即视为手势轮廓。
- 将提取的轮廓直接与标准图片进行匹配,从而简化识别过程。
但在匹配过程中发现,“剪刀”手势常与“石头”或“布”的手势混淆。为此,在匹配基础上增加了矩形框架面积的判断,通常遵循面积规律:石头 < 剪刀 < 布,代码如下:
# 轮廓匹配
value = [0, 0, 0]
value[0] = cv2.matchShapes(img_contour, img1, 1, 0.0)
value[1] = cv2.matchShapes(img_contour, img2, 1, 0.0)
value[2] = cv2.matchShapes(img_contour, img3, 1, 0.0)
min_index = np.argmin(value)
if min_index == 0: # 剪刀
print(text[int(min_index)], value)
elif min_index == 1 and w*h < 25000: # 石头
print(text[int(min_index)], value)
elif min_index == 1 and w*h >= 25000: # 剪刀
print(text[0], value)
elif min_index == 2 and w * h > 30000: # 布
print(text[int(min_index)], value)
elif min_index == 2 and w * h <= 30000: # 剪刀
print(text[0], value)
程序根据匹配值和面积大小综合判定识别结果。例如,匹配值1.179515828609219、0.9604643714904955、0.9896353720020925分别对应剪刀、石头、布的匹配得分,数值越小表示匹配度越高。结合最终识别情况,石头的识别成功率最高,约为98%;布次之,约为88%;剪刀最低,约为80%。此外,结果易受环境亮度影响,过暗或过亮时可能导致轮廓无法提取,表明该方法仍有改进空间,未来可考虑引入机器学习方法。
石头 [1.179515828609219, 0.9604643714904955, 0.9896353720020925]
程序运行效果如下,黄色矩形框标示识别区域,gesture窗口显示用于轮廓匹配的手势图。



至此,关于OpenCV+Python3.5简易手势识别的实现介绍完毕,更多相关OpenCV手势识别内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









