Python实现手势识别


Posted in Python onOctober 21, 2020

这是借鉴了github上的一个源程序,参考源:https://github.com/lzane/Fingers-Detection-using-OpenCV-and-Python

自己在这个基础上做了一点修改补充后,可以实现手指指尖的检测,并且可以在windows系统下通过判断手指数目,来模拟键盘操作。下面直接上源程序,并做了详细注释,方便理解。

环境:python3.6+opencv3.4.0

代码如下:

import cv2
import numpy as np
import copy
import math
import win32api
import win32con

# 参数
cap_region_x_begin = 0.5 # 起点/总宽度
cap_region_y_end = 0.8
threshold = 60 # 二值化阈值
blurValue = 41 # 高斯模糊参数
bgSubThreshold = 50
learningRate = 0

# 变量
isBgCaptured = 0 # 布尔类型, 背景是否被捕获
triggerSwitch = False # 如果正确,键盘模拟器将工作


def printThreshold(thr):
  print("! Changed threshold to " + str(thr))


def removeBG(frame): #移除背景
  fgmask = bgModel.apply(frame, learningRate=learningRate) #计算前景掩膜
  kernel = np.ones((3, 3), np.uint8)
  fgmask = cv2.erode(fgmask, kernel, iterations=1) #使用特定的结构元素来侵蚀图像。
  res = cv2.bitwise_and(frame, frame, mask=fgmask) #使用掩膜移除静态背景
  return res

# 相机/摄像头
camera = cv2.VideoCapture(0)  #打开电脑自带摄像头,如果参数是1会打开外接摄像头
camera.set(10, 200)  #设置视频属性
cv2.namedWindow('trackbar') #设置窗口名字
cv2.resizeWindow("trackbar", 640, 200) #重新设置窗口尺寸
cv2.createTrackbar('threshold', 'trackbar', threshold, 100, printThreshold)
#createTrackbar是Opencv中的API,其可在显示图像的窗口中快速创建一个滑动控件,用于手动调节阈值,具有非常直观的效果。

while camera.isOpened():
  ret, frame = camera.read()
  threshold = cv2.getTrackbarPos('threshold', 'trackbar') #返回滑动条上的位置的值(即实时更新阈值)
  # frame = cv2.cvtColor(frame,cv2.COLOR_RGB2YCrCb)
  frame = cv2.bilateralFilter(frame, 5, 50, 100) # 双边滤波
  frame = cv2.flip(frame, 1) # 翻转 0:沿X轴翻转(垂直翻转)  大于0:沿Y轴翻转(水平翻转)  小于0:先沿X轴翻转,再沿Y轴翻转,等价于旋转180°
  cv2.rectangle(frame, (int(cap_region_x_begin * frame.shape[1]), 0),(frame.shape[1], int(cap_region_y_end * frame.shape[0])), (0, 0, 255), 2)
  #画矩形框 frame.shape[0]表示frame的高度  frame.shape[1]表示frame的宽度  注:opencv的像素是BGR顺序
  cv2.imshow('original', frame)  #经过双边滤波后的初始化窗口

  #主要操作
  if isBgCaptured == 1: # isBgCaptured == 1 表示已经捕获背景
    img = removeBG(frame) #移除背景
    img = img[0:int(cap_region_y_end * frame.shape[0]),int(cap_region_x_begin * frame.shape[1]):frame.shape[1]] # 剪切右上角矩形框区域
    cv2.imshow('mask', img)

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #将移除背景后的图像转换为灰度图
    blur = cv2.GaussianBlur(gray, (blurValue, blurValue), 0) #加高斯模糊
    cv2.imshow('blur', blur)
    ret, thresh = cv2.threshold(blur, threshold, 255, cv2.THRESH_BINARY) #二值化处理
    cv2.imshow('binary', thresh)

    # get the coutours
    thresh1 = copy.deepcopy(thresh)
    _, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    #寻找轮廓  注:这里的'_'用作变量名称,_表示一个变量被指定了名称,但不打算使用。
    length = len(contours)
    maxArea = -1
    if length > 0:
      for i in range(length): # 找到最大的轮廓(根据面积)
        temp = contours[i]
        area = cv2.contourArea(temp) #计算轮廓区域面积
        if area > maxArea:
          maxArea = area
          ci = i

      res = contours[ci] #得出最大的轮廓区域
      hull = cv2.convexHull(res) #得出点集(组成轮廓的点)的凸包
      drawing = np.zeros(img.shape, np.uint8)
      cv2.drawContours(drawing, [res], 0, (0, 255, 0), 2)  #画出最大区域轮廓
      cv2.drawContours(drawing, [hull], 0, (0, 0, 255), 3) #画出凸包轮廓

      moments = cv2.moments(res) # 求最大区域轮廓的各阶矩
      center = (int(moments['m10'] / moments['m00']), int(moments['m01'] / moments['m00']))
      cv2.circle(drawing, center, 8, (0,0,255), -1)  #画出重心

      fingerRes = []  #寻找指尖
      max = 0; count = 0; notice = 0; cnt = 0
      for i in range(len(res)):
        temp = res[i]
        dist = (temp[0][0] -center[0])*(temp[0][0] -center[0]) + (temp[0][1] -center[1])*(temp[0][1] -center[1]) #计算重心到轮廓边缘的距离
        if dist > max:
          max = dist
          notice = i
        if dist != max:
          count = count + 1
          if count > 40:
            count = 0
            max = 0
            flag = False  #布尔值
            if center[1] < res[notice][0][1]:  #低于手心的点不算
              continue
            for j in range(len(fingerRes)): #离得太近的不算
              if abs(res[notice][0][0]-fingerRes[j][0]) < 20 :
                flag = True
                break
            if flag :
              continue
            fingerRes.append(res[notice][0])
            cv2.circle(drawing, tuple(res[notice][0]), 8 , (255, 0, 0), -1) #画出指尖
            cv2.line(drawing, center, tuple(res[notice][0]), (255, 0, 0), 2)
            cnt = cnt + 1

      cv2.imshow('output', drawing)
      print(cnt)
      if triggerSwitch is True:
        if cnt >= 3:
          print(cnt)
          # app('System Events').keystroke(' ') # simulate pressing blank space
          win32api.keybd_event(32, 0, 0, 0) # 空格键位码是32
          win32api.keybd_event(32, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放空格键

  # 输入的键盘值
  k = cv2.waitKey(10)
  if k == 27: # 按下ESC退出
    break
  elif k == ord('b'): # 按下'b'会捕获背景
    bgModel = cv2.createBackgroundSubtractorMOG2(0, bgSubThreshold)
    #Opencv集成了BackgroundSubtractorMOG2用于动态目标检测,用到的是基于自适应混合高斯背景建模的背景减除法。
    isBgCaptured = 1
    print('!!!Background Captured!!!')
  elif k == ord('r'): # 按下'r'会重置背景
    bgModel = None
    triggerSwitch = False
    isBgCaptured = 0
    print('!!!Reset BackGround!!!')
  elif k == ord('n'):
    triggerSwitch = True
    print('!!!Trigger On!!!')

运行程序操作:运行程序后,按下键盘的 b 键就可以捕获背景了

运行结果:

Python实现手势识别

注:模拟点击空格键部分并未展示出来,有兴趣的可以尝试一下(按下n键就可以模拟键盘操作了)

补:该程序受光线影响其实较大,只有在单调背景小效果很好。

-------------------补充----------------------

后期再运行该程序的时候发现有一个错误,如下:

Python实现手势识别

原因:opencv版本的原因,在opencv 4.0.0版本后,findContours的返回值只有contours, hierarchy两个参数,不再有三个参数了!

解决办法:

方法一:

更换opencv的版本 

方法二:

将代码 _,contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  改为 contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  即可!

以上就是Python实现手势识别的详细内容,更多关于Python 手势识别的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
用PyQt进行Python图形界面的程序的开发的入门指引
Apr 14 Python
在Python中处理字符串之isdecimal()方法的使用
May 20 Python
Python黑魔法@property装饰器的使用技巧解析
Jun 16 Python
python使用opencv读取图片的实例
Aug 17 Python
python使用标准库根据进程名如何获取进程的pid详解
Oct 31 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
Jul 27 Python
python获取微信企业号打卡数据并生成windows计划任务
Apr 30 Python
Python matplotlib学习笔记之坐标轴范围
Jun 28 Python
python移位运算的实现
Jul 15 Python
python批量解压zip文件的方法
Aug 20 Python
Python基于进程池实现多进程过程解析
Apr 30 Python
使用Python Tkinter实现剪刀石头布小游戏功能
Oct 23 Python
利用Python优雅的登录校园网
Oct 21 #Python
python 使用三引号时容易犯的小错误
Oct 21 #Python
利用Python实现字幕挂载(把字幕文件与视频合并)思路详解
Oct 21 #Python
基于python爬取链家二手房信息代码示例
Oct 21 #Python
如何让PyQt5中QWebEngineView与JavaScript交互
Oct 21 #Python
Python为何不支持switch语句原理详解
Oct 21 #Python
基于Python爬取素材网站音频文件
Oct 21 #Python
You might like
PHP获取当前文件所在目录 getcwd()函数
2009/05/13 PHP
一组PHP加密解密函数分享
2014/06/05 PHP
Javascript this关键字使用分析
2008/10/21 Javascript
关于viewport,Ext.panel和Ext.form.panel的关系
2009/05/07 Javascript
js检查页面上有无重复id的实现代码
2013/07/17 Javascript
JS+CSS实现弹出全屏灰黑色透明遮罩效果的方法
2014/12/20 Javascript
JavaScript中的数据类型转换方法小结
2015/10/26 Javascript
js判断手机访问或者PC的几个例子(常用于手机跳转)
2015/12/15 Javascript
JavaScript数组方法总结分析
2016/05/06 Javascript
Angular2内置指令NgFor和NgIf详解
2016/08/03 Javascript
JavaScript中运算符规则和隐式类型转换示例详解
2017/09/06 Javascript
JavaScript实现简单动态进度条效果
2018/04/06 Javascript
Vue实现点击时间获取时间段查询功能
2020/08/21 Javascript
原生js+ajax分页组件
2020/01/30 Javascript
Vue通过getAction的finally来最大程度避免影响主数据呈现问题
2020/04/24 Javascript
python字符串排序方法
2014/08/29 Python
Python中Continue语句的用法的举例详解
2015/05/14 Python
python快速建立超简单的web服务器的实现方法
2018/02/17 Python
Pycharm设置utf-8自动显示方法
2019/01/17 Python
PyTorch中常用的激活函数的方法示例
2019/08/20 Python
html5视频媒体标签video的使用方法及完整参数说明详解
2019/09/27 HTML / CSS
Silk’n激光脱毛器官网:silkn.com
2016/10/06 全球购物
英国最受欢迎的手表网站:Watch Shop
2016/10/21 全球购物
捷克母婴用品购物网站:Feedo.cz
2020/12/28 全球购物
环境科学专业个人求职的自我评价
2013/11/28 职场文书
计算机专业学生求职信分享
2013/12/15 职场文书
促销活动策划方案
2014/01/12 职场文书
公职人员索取回扣检举信
2014/04/04 职场文书
个人总结与自我评价
2014/09/18 职场文书
企业务虚会发言材料
2014/10/20 职场文书
杜甫草堂导游词
2015/02/03 职场文书
军训个人总结
2015/03/03 职场文书
大客户经理岗位职责
2015/04/09 职场文书
大学毕业典礼致辞
2015/07/29 职场文书
python opencv旋转图片的使用方法
2021/06/04 Python
MySQL读取JSON转换的方式
2022/03/18 MySQL