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抓取网页内容示例分享
Feb 24 Python
使用Python标准库中的wave模块绘制乐谱的简单教程
Mar 30 Python
收藏整理的一些Python常用方法和技巧
May 18 Python
Python编程中用close()方法关闭文件的教程
May 24 Python
Python正则表达式分组概念与用法详解
Jun 24 Python
python如何读写json数据
Mar 21 Python
Python paramiko模块的使用示例
Apr 11 Python
python实现录音小程序
Oct 26 Python
Python 实现域名解析为ip的方法
Feb 14 Python
python中如何使用分步式进程计算详解
Mar 22 Python
Python+OpenCV+pyQt5录制双目摄像头视频的实例
Jun 28 Python
解决python ThreadPoolExecutor 线程池中的异常捕获问题
Apr 08 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 5.3.5安装memcache注意事项小结
2011/04/12 PHP
第4章 数据处理-php数组的处理-郑阿奇
2011/07/04 PHP
php计算当前程序执行时间示例
2014/04/24 PHP
PHP curl实现抓取302跳转后页面的示例
2014/07/04 PHP
PHP基于关联数组20行代码搞定约瑟夫问题示例
2017/11/07 PHP
用脚本调用样式的几种方法
2006/12/09 Javascript
JSuggest自动匹配下拉框使用方法(示例代码)
2013/12/27 Javascript
js获取checkbox值的方法
2015/01/28 Javascript
jQuery插件Elastislide实现响应式的焦点图无缝滚动切换特效
2015/04/12 Javascript
javascript实现日期按月份加减
2015/05/15 Javascript
Javascript控制div属性动态变化实例分析
2015/10/08 Javascript
JS在onclientclick里如何控制onclick的执行
2016/05/30 Javascript
url中的特殊符号有什么含义(推荐)
2016/06/17 Javascript
jquery实现左右轮播切换效果
2018/01/01 jQuery
ajax请求data遇到的问题分析
2018/01/18 Javascript
Node.js爬取豆瓣数据实例分析
2018/03/05 Javascript
Angular中使用better-scroll插件的方法
2018/03/27 Javascript
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
[03:52]显微镜下的DOTA2第三期——英雄在无聊的时候干什么
2014/06/20 DOTA
[45:15]Optic vs VP 2018国际邀请赛淘汰赛BO3 第一场 8.24
2018/08/25 DOTA
python3 读取Excel表格中的数据
2018/10/16 Python
用Python中的turtle模块画图两只小羊方法
2019/04/09 Python
Python3 Click模块的使用方法详解
2020/02/12 Python
keras 获取某层输出 获取复用层的多次输出实例
2020/05/23 Python
基于Python实现视频的人脸融合功能
2020/06/12 Python
python进度条显示之tqmd模块
2020/08/22 Python
美国隐形眼镜网:Major Lens
2018/02/09 全球购物
Tessabit美国:集世界奢侈品和设计师品牌的意大利精品买手店
2020/06/29 全球购物
医学生个人求职信范文
2013/09/24 职场文书
求职信写作要突出重点
2014/01/01 职场文书
班级活动策划书
2014/02/06 职场文书
保险专业自荐信范文
2014/02/20 职场文书
财务人员岗位职责
2015/02/03 职场文书
晚会主持人开场白台词
2015/05/28 职场文书
学习经验交流会总结
2015/11/02 职场文书
从结婚开始的恋爱故事。小说《我的美好婚事》TV动画化决定
2022/04/07 日漫