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 相关文章推荐
Python编程中用close()方法关闭文件的教程
May 24 Python
Python读写unicode文件的方法
Jul 10 Python
使用PyInstaller将python转成可执行文件exe笔记
May 26 Python
对Python 数组的切片操作详解
Jul 02 Python
python 内置模块详解
Jan 01 Python
python3+selenium实现qq邮箱登陆并发送邮件功能
Jan 23 Python
Python日志无延迟实时写入的示例
Jul 11 Python
Django框架 查询Extra功能实现解析
Sep 04 Python
Python内置数据类型list各方法的性能测试过程解析
Jan 07 Python
python2 对excel表格操作完整示例
Feb 23 Python
Python3 用matplotlib绘制sigmoid函数的案例
Dec 11 Python
python实现图片转字符画
Feb 19 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
mysql 中InnoDB和MyISAM的区别分析小结
2008/04/15 PHP
PHP 操作文件的一些FAQ总结
2009/02/12 PHP
Windows下安装Memcached的步骤说明
2010/04/25 PHP
php中使用Curl、socket、file_get_contents三种方法POST提交数据
2011/08/12 PHP
php调用淘宝开放API实现根据卖家昵称获取卖家店铺ID的方法
2015/07/29 PHP
php语言的7种基本的排序方法
2020/12/28 PHP
通过PHP实现获取访问用户IP
2020/05/09 PHP
IE6浏览器下resize事件被执行了多次解决方法
2012/12/11 Javascript
jquery中.add()的使用分析
2013/04/26 Javascript
Js表格万条数据瞬间加载实现代码
2014/02/20 Javascript
javascript内置对象arguments详解
2014/03/16 Javascript
JS实现页面超时后自动跳转到登陆页面
2015/01/19 Javascript
JQuery 传送中文乱码问题的简单解决办法
2016/05/24 Javascript
巧用Vue.js+Vuex制作专门收藏微信公众号的app
2016/11/03 Javascript
js实现截图保存图片功能的代码示例
2017/02/16 Javascript
Javascript中八种遍历方法的执行速度深度对比
2017/04/25 Javascript
vue.js评论发布信息可插入QQ表情功能
2017/08/08 Javascript
js注册时输入合法性验证方法
2017/10/21 Javascript
Vue中util的工具函数实例详解
2019/07/08 Javascript
element form 校验数组每一项实例代码
2019/10/10 Javascript
[02:56]DOTA2矮人直升机 英雄基础教程
2013/11/26 DOTA
[35:43]2018DOTA2亚洲邀请赛 4.1 小组赛B组 paiN vs Effect
2018/04/03 DOTA
Python函数参数类型*、**的区别
2015/04/11 Python
python使用wxpython开发简单记事本的方法
2015/05/20 Python
解决Matplotlib图表不能在Pycharm中显示的问题
2018/05/24 Python
快速解决jupyter notebook启动需要密码的问题
2020/04/21 Python
css3 clip实现圆环进度条的示例代码
2018/02/07 HTML / CSS
美国鞋类购物网站:Shiekh Shoes
2016/08/21 全球购物
Perfume’s Club法国站:购买香水和化妆品
2019/05/02 全球购物
JDO的含义
2012/11/17 面试题
LINUX下线程,GDI类的解释
2012/04/17 面试题
初中新生军训方案
2014/05/13 职场文书
大学生作弊检讨书
2014/09/11 职场文书
个人工作作风整改措施思想汇报
2014/10/13 职场文书
营销计划书
2015/01/17 职场文书
处世之道:关于真诚相待的名言推荐
2019/12/02 职场文书