美文网首页
手势识别流程及实现

手势识别流程及实现

作者: 大龙10 | 来源:发表于2023-10-13 12:46 被阅读0次

书名:计算机视觉40例从入门到深度学习:OpenCV-Python
作者:李立宗
出版社:电子工业出版社
出版时间:2022-07-01
ISBN:9787121436857


一、手势识别流程

  • 手势识别基本流程图如图8-9所示。
图8-9 手势识别基本流程图
  • Step 1:获取图像。
    本步骤的主要任务是读取摄像头、划定识别区域。划定识别区域的目的在于仅识别特定区域内的手势,简化识别过程。
  • Step 2:识别皮肤.
    主要任务是色彩空间转换、在新的色彩空间内根据颜色范围值识别出皮肤所在区域。
    色彩空间转换的目的在于将图像从BGR色彩空间转换到HSV色彩空间,以进行皮肤检测。通过皮肤颜色的范围值确定手势所在区域。
  • Step 3:图像预处理。
    图像预处理主要是为了去除图像内的噪声,以便后续处理。这里的图像预处理包含膨胀操作和高斯滤波
  • Step 4:获取轮廓。
    主要任务在于获取图像的轮廓信息,并获取其面积
  • Step 5:获取凸包。
    主要任务是获取轮廓的凸包信息,并获取其面积.
    本步骤获取了凸包的面积,在后续步骤中用轮廓面积与凸包面积的比值来识别表示数值0的手势和表示数值1的手势。
  • Step 6:计算轮廓和凸包的面积比。
    主要任务是计算轮廓和凸包的面积比
    本步骤获取了轮廓与凸包的面积比,根据该值与阈值(通常为0.9)的关系,识别表示数值0的手势和表示数值1的手势。
  • Step 7:获取凸缺陷。
    主要任务是获取手势的凸缺陷
    通过函数cv2.convexHull、cv2.convexityDefects获取凸缺陷。
  • Step 8:计算并绘制有效凸缺陷。
    主要任务是计算有效凸缺陷的个数,并绘制凸包、凸缺陷的最远点
    本步骤根据凸缺陷中的距离和角度排除了噪声的影响。
  • Step 9:使用凸缺陷识别手势。
    主要任务是根据凸缺陷的个数、凸缺陷与凸包的面积比进行手势识别
    本步骤先对凸缺陷的个数进行判断,然后根据凸缺陷的个数判定当前手势的形状。有一个特例是,当凸缺陷的个数为0时,需要再对轮廓与凸包面积比进行判断,才能决定具体手势。
  • Step 10:显示结果。
    主要任务是将识别结果显示出来

二、程序实现

# -*- coding: utf-8 -*-
"""
Created on Sat Oct 14 10:25:19 2023

@author: Administrator
"""

import numpy as np
import cv2
import math

cap=cv2.VideoCapture(0,cv2.CAP_DSHOW)
# ======主程序======
while ( cap.isOpened() ):
   ret,frame = cap.read()     # 读取摄像头图像
   # print(frame.shape)       # 获取窗口大小
   frame = cv2.flip(frame,1)  # 绕着 y轴方向翻转图像

   # ====设定一个固定区域作为识别区域==
   roi = frame[10:210,0:200]    # 将右上角设置为固定识别区域
   cv2.rectangle(frame,(0,10),(200,210),(0,0,255),0) # 将选定的区域标记出来

   # ====在hsv色彩空间内检测出皮肤======
   hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)        #色彩空间转换
   lower_skin = np.array([0,28,70],dtype=np.uint8)  # 设定范围,下限
   upper_skin = np.array([20,255,255],dtype=np.uint8) # 设定范围,上限
   mask = cv2.inRange(hsv,lower_skin,upper_skin)     # 确定手势所在区域

   # =====预处理====#
   kernel = np.ones((2,2),np.uint8)             # 构造一个核
   mask = cv2.dilate(mask,kernel,iterations=4)  # 膨胀操作
   mask = cv2.GaussianBlur(mask,(5,5),100)     # 高斯滤波

   #=========找出轮廓========
   # 查找所有轮廓
   contours, h =cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  

   #从所有轮廓中找到最大的轮廓,并将其作为手势轮廓
   cnt= max(contours,key=lambda x:cv2.contourArea(x))
   areacnt = cv2.contourArea(cnt)   #获取轮廓面积

   # =========获取轮廓的凸包=========
   hull = cv2.convexHull(cnt)       #获取轮廓的凸包,用于计算面积,返回坐标值
   areahull = cv2.contourArea(hull)  #获取凸包的面积
   
   # ==获取轮廓面积、凸包面积,并计算二者的比值====
   arearatio = areacnt/areahull

   # 轮廓面积/凸包面积
   # 大于0.9,表示二者面积几乎一致,是手势 0
   # 否则,说明凸缺陷较大,是手势 1.

   # ======获取凸缺陷=============
   hull=cv2.convexHull(cnt,returnPoints=False) # 使用索引,returnPoints=False
   defects = cv2.convexityDefects(cnt,hull) # 获取凸缺陷
   
   # ======凸缺陷处理=============
   n=0           # 定义凹凸点个数初始值为 0
   
   # -------遍历凸缺陷,判断是否为手指间的凸缺陷----
   
   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])
       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((b**2 + c**2 -a**2)/(2*b*c))*57
       # -----------绘制手指间的凸包最远点--
       # 角度介于 20°~90° 的认为是不同手指构成的凸缺陷
       if angle<=90 and d>20:
           n+=1
           cv2.circle(roi,far,3,[255,0,0],-1) # 用蓝色绘制最远点
       # ------绘制手势的凸包------
       cv2.line(roi,start,end,[0,255,0],2)  
       
   # ======通过凸缺陷个数及凸缺陷和凸包的面积比判断识别结果========
   if n==0:      # 0个凸缺陷,手势可能表示数值 0,也可能表示数值 1
       if arearatio>0.9:
           result='0'  # 轮廓面积/凸包面积>0.9,判定为拳头,识别手势为数值 0
       else:
           result='1'  # 轮廓面积/凸包面积≤0.9,说明存在很大的凸缺陷,识别手势为数值 1
   elif n==1:
       result='2'  # 1个凸缺陷,对应2 根手指,识别手势为数值 2
   elif n==2:
       result='3'  # 2个凸缺陷,对应3 根手指,识别手势为数值3
   elif n==3:
       result='4'  # 3 个凸缺陷,对应4 根手指,识别手势为数值 4
   elif n==4:
       result='5'  # 4个凸缺陷,对应5根手指,识别手势为数值 5

   # ====设置与显示识别结果相关的参数=======
   org=(0,80)
   font = cv2.FONT_HERSHEY_PLAIN
   fontScale=2
   color=(0,0,255)
   thickness=3
   # ====显示识别结果======
   cv2.putText(frame,result,org,font,fontScale,color,thickness)
   cv2.imshow('frame',frame)
   k = cv2.waitKey(25) & 0xff
   if k == 27:                # 按下“Esc”键退出
       break
   
cv2.destroyAllWindows()
cap.release()
 
运行结果

相关文章

网友评论

      本文标题:手势识别流程及实现

      本文链接:https://www.haomeiwen.com/subject/aewibdtx.html