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写的ARP攻击代码实例
Jun 04 Python
在Django中编写模版节点及注册标签的方法
Jul 20 Python
Flask框架的学习指南之开发环境搭建
Nov 20 Python
Python统计python文件中代码,注释及空白对应的行数示例【测试可用】
Jul 25 Python
Python嵌套式数据结构实例浅析
Mar 05 Python
树莓派3 搭建 django 服务器的实例
Aug 29 Python
Pytorch 保存模型生成图片方式
Jan 10 Python
基于Python获取照片的GPS位置信息
Jan 20 Python
详解Python IO口多路复用
Jun 17 Python
Python模块常用四种安装方式
Oct 20 Python
基于Python爬取京东双十一商品价格曲线
Oct 23 Python
Python 多线程处理任务实例
Nov 07 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动态图像的创建
2006/10/09 PHP
PHP把网页保存为word文件的三种方法
2014/04/01 PHP
mac环境中使用brew安装php5.5.15
2014/08/18 PHP
phpmyadmin提示The mbstring extension is missing的解决方法
2014/12/17 PHP
微信公众平台开发教程②微信端分享功能图文详解
2019/04/10 PHP
juqery 学习之三 选择器 可见性 元素属性
2010/11/25 Javascript
javascript 文本框水印/占位符(watermark/placeholder)实现方法
2012/01/15 Javascript
jquery ajax同步异步的执行最终解决方案
2013/04/26 Javascript
js innerHTML 改变div内容的方法
2013/08/03 Javascript
nodejs中使用monk访问mongodb
2014/07/06 NodeJs
javascript使用avalon绑定实现checkbox全选
2015/05/06 Javascript
javascript中Date()函数在各浏览器中的显示效果
2015/06/18 Javascript
jquery实现鼠标滑过后动态图片提示效果实例
2015/08/10 Javascript
js调用webservice构造SOAP进行身份验证
2016/04/27 Javascript
jquery.Jcrop结合JAVA后台实现图片裁剪上传实例
2016/11/05 Javascript
JavaScript实现256色转灰度图
2017/02/22 Javascript
JavaScript基本类型值-Undefined、Null、Boolean
2017/02/23 Javascript
vue2笔记 — vue-router路由懒加载的实现
2017/03/03 Javascript
JS如何设置元素样式的方法示例
2017/08/28 Javascript
JsChart组件使用详解
2018/03/04 Javascript
有关vue 开发钉钉 H5 微应用 dd.ready() 不执行问题及快速解决方案
2020/05/09 Javascript
three.js如何实现3D动态文字效果
2021/03/03 Javascript
[01:29]2017 DOTA2国际邀请赛官方英雄手办展示
2017/03/18 DOTA
Python实现的Kmeans++算法实例
2014/04/26 Python
python字符串对其居中显示的方法
2015/07/11 Python
用Python写冒泡排序代码
2016/04/12 Python
python的几种矩阵相乘的公式详解
2019/07/10 Python
django之状态保持-使用redis存储session的例子
2019/07/28 Python
基于Python爬取fofa网页端数据过程解析
2020/07/13 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
屈臣氏菲律宾官网:Watsons菲律宾
2020/06/30 全球购物
如何用Python来进行查询和替换一个文本字符串
2014/01/02 面试题
优秀的茶餐厅创业计划书
2014/01/03 职场文书
安全责任书范本
2014/04/15 职场文书
二胎满月酒致辞
2015/07/29 职场文书
2016年清明节期间群众祭祀活动工作总结
2016/04/01 职场文书