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

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

  环境:python3.6+opencv3.4.0

代码如下:

  1. import cv2
  2. import numpy as np
  3. import copy
  4. import math
  5. import win32api
  6. import win32con
  7.  
  8. # 参数
  9. cap_region_x_begin = 0.5 # 起点/总宽度
  10. cap_region_y_end = 0.8
  11. threshold = 60 # 二值化阈值
  12. blurValue = 41 # 高斯模糊参数
  13. bgSubThreshold = 50
  14. learningRate = 0
  15.  
  16. # 变量
  17. isBgCaptured = 0 # 布尔类型, 背景是否被捕获
  18. triggerSwitch = False # 如果正确,键盘模拟器将工作
  19.  
  20. def printThreshold(thr):
  21. print("! Changed threshold to " + str(thr))
  22.  
  23. def removeBG(frame): #移除背景
  24. fgmask = bgModel.apply(frame, learningRate=learningRate) #计算前景掩膜
  25. kernel = np.ones((3, 3), np.uint8)
  26. fgmask = cv2.erode(fgmask, kernel, iterations=1) #使用特定的结构元素来侵蚀图像。
  27. res = cv2.bitwise_and(frame, frame, mask=fgmask) #使用掩膜移除静态背景
  28. return res
  29.  
  30. # 相机/摄像头
  31. camera = cv2.VideoCapture(0) #打开电脑自带摄像头,如果参数是1会打开外接摄像头
  32. camera.set(10, 200) #设置视频属性
  33. cv2.namedWindow('trackbar') #设置窗口名字
  34. cv2.resizeWindow("trackbar", 640, 200) #重新设置窗口尺寸
  35. cv2.createTrackbar('threshold', 'trackbar', threshold, 100, printThreshold)
  36. #createTrackbar是Opencv中的API,其可在显示图像的窗口中快速创建一个滑动控件,用于手动调节阈值,具有非常直观的效果。
  37.  
  38. while camera.isOpened():
  39. ret, frame = camera.read()
  40. threshold = cv2.getTrackbarPos('threshold', 'trackbar') #返回滑动条上的位置的值(即实时更新阈值)
  41. # frame = cv2.cvtColor(frame,cv2.COLOR_RGB2YCrCb)
  42. frame = cv2.bilateralFilter(frame, 5, 50, 100) # 双边滤波
  43. frame = cv2.flip(frame, 1) # 翻转 0:沿X轴翻转(垂直翻转) 大于0:沿Y轴翻转(水平翻转) 小于0:先沿X轴翻转,再沿Y轴翻转,等价于旋转180°
  44. 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)
  45. #画矩形框 frame.shape[0]表示frame的高度 frame.shape[1]表示frame的宽度 注:opencv的像素是BGR顺序
  46. cv2.imshow('original', frame) #经过双边滤波后的初始化窗口
  47.  
  48. #主要操作
  49. if isBgCaptured == 1: # isBgCaptured == 1 表示已经捕获背景
  50. img = removeBG(frame) #移除背景
  51. img = img[0:int(cap_region_y_end * frame.shape[0]),int(cap_region_x_begin * frame.shape[1]):frame.shape[1]] # 剪切右上角矩形框区域
  52. cv2.imshow('mask', img)
  53.  
  54. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #将移除背景后的图像转换为灰度图
  55. blur = cv2.GaussianBlur(gray, (blurValue, blurValue), 0) #加高斯模糊
  56. cv2.imshow('blur', blur)
  57. ret, thresh = cv2.threshold(blur, threshold, 255, cv2.THRESH_BINARY) #二值化处理
  58. cv2.imshow('binary', thresh)
  59.  
  60. # get the coutours
  61. thresh1 = copy.deepcopy(thresh)
  62. _, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  63. #寻找轮廓 注:这里的'_'用作变量名称,_表示一个变量被指定了名称,但不打算使用。
  64. length = len(contours)
  65. maxArea = -1
  66. if length > 0:
  67. for i in range(length): # 找到最大的轮廓(根据面积)
  68. temp = contours[i]
  69. area = cv2.contourArea(temp) #计算轮廓区域面积
  70. if area > maxArea:
  71. maxArea = area
  72. ci = i
  73.  
  74. res = contours[ci] #得出最大的轮廓区域
  75. hull = cv2.convexHull(res) #得出点集(组成轮廓的点)的凸包
  76. drawing = np.zeros(img.shape, np.uint8)
  77. cv2.drawContours(drawing, [res], 0, (0, 255, 0), 2) #画出最大区域轮廓
  78. cv2.drawContours(drawing, [hull], 0, (0, 0, 255), 3) #画出凸包轮廓
  79.  
  80. moments = cv2.moments(res) # 求最大区域轮廓的各阶矩
  81. center = (int(moments['m10'] / moments['m00']), int(moments['m01'] / moments['m00']))
  82. cv2.circle(drawing, center, 8, (0,0,255), -1) #画出重心
  83.  
  84. fingerRes = [] #寻找指尖
  85. max = 0; count = 0; notice = 0; cnt = 0
  86. for i in range(len(res)):
  87. temp = res[i]
  88. dist = (temp[0][0] -center[0])*(temp[0][0] -center[0]) + (temp[0][1] -center[1])*(temp[0][1] -center[1]) #计算重心到轮廓边缘的距离
  89. if dist > max:
  90. max = dist
  91. notice = i
  92. if dist != max:
  93. count = count + 1
  94. if count > 40:
  95. count = 0
  96. max = 0
  97. flag = False #布尔值
  98. if center[1] < res[notice][0][1]: #低于手心的点不算
  99. continue
  100. for j in range(len(fingerRes)): #离得太近的不算
  101. if abs(res[notice][0][0]-fingerRes[j][0]) < 20 :
  102. flag = True
  103. break
  104. if flag :
  105. continue
  106. fingerRes.append(res[notice][0])
  107. cv2.circle(drawing, tuple(res[notice][0]), 8 , (255, 0, 0), -1) #画出指尖
  108. cv2.line(drawing, center, tuple(res[notice][0]), (255, 0, 0), 2)
  109. cnt = cnt + 1
  110.  
  111. cv2.imshow('output', drawing)
  112. print(cnt)
  113. if triggerSwitch is True:
  114. if cnt >= 3:
  115. print(cnt)
  116. # app('System Events').keystroke(' ') # simulate pressing blank space
  117. win32api.keybd_event(32, 0, 0, 0) # 空格键位码是32
  118. win32api.keybd_event(32, 0, win32con.KEYEVENTF_KEYUP, 0) # 释放空格键
  119.  
  120. # 输入的键盘值
  121. k = cv2.waitKey(10)
  122. if k == 27: # 按下ESC退出
  123. break
  124. elif k == ord('b'): # 按下'b'会捕获背景
  125. bgModel = cv2.createBackgroundSubtractorMOG2(0, bgSubThreshold)
  126. #Opencv集成了BackgroundSubtractorMOG2用于动态目标检测,用到的是基于自适应混合高斯背景建模的背景减除法。
  127. isBgCaptured = 1
  128. print('!!!Background Captured!!!')
  129. elif k == ord('r'): # 按下'r'会重置背景
  130. bgModel = None
  131. triggerSwitch = False
  132. isBgCaptured = 0
  133. print('!!!Reset BackGround!!!')
  134. elif k == ord('n'):
  135. triggerSwitch = True
  136. print('!!!Trigger On!!!')

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

运行结果:

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

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

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

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

原因: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手势识别的更多相关文章

  1. Python手势识别与控制

    代码地址如下:http://www.demodashi.com/demo/12968.html Python手势识别与控制 概述 本文中的手势识别与控制功能主要采用 OpenCV 库实现, OpenC ...

  2. Python C++ OpenCV TensorFlow手势识别(1-10) 毕设 定制开发

    Python C++ OpenCV TensorFlow手势识别(1-10) 毕设 支持定制开发 (MFC,QT, PyQt5界面,视频摄像头识别) QQ: 3252314061 效果如下:

  3. Python爬取CSDN博客文章

    0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...

  4. 【NLP】3000篇搜狐新闻语料数据预处理器的python实现

    3000篇搜狐新闻语料数据预处理器的python实现 白宁超 2017年5月5日17:20:04 摘要: 关于自然语言处理模型训练亦或是数据挖掘.文本处理等等,均离不开数据清洗,数据预处理的工作.这里 ...

  5. 基于ssd的手势识别模型(object detection api方式)

    [Tensorflow]Object Detection API-训练自己的手势识别模型 1. 安装tensorflow以及下载object detection api 1.安装tensorflow: ...

  6. 超声波手势识别(STM32四路超声波获取)

    超声波手势识别在市场上已经有见实现,但研究其传感器发现并不是市场上随意可见的,如果暂且考虑成本,该如何入门实现简单的手势识别呢.聊天中老师给出一个很好的提议,就是固定四个超声波,分别为上下左右,然后进 ...

  7. Python中调用自然语言处理工具HanLP手记

    手记实用系列文章: 1 结巴分词和自然语言处理HanLP处理手记 2 Python中文语料批量预处理手记 3 自然语言处理手记 4 Python中调用自然语言处理工具HanLP手记 5 Python中 ...

  8. hanlp自然语言处理包的基本使用--python

    hanlp拥有:中文分词.命名实体识别.摘要关键字.依存句法分析.简繁拼音转换.智能推荐. 这里主要介绍一下hanlp的中文分词.命名实体识别.依存句法分析,这里就不介绍具体的hanlp的安装了,百度 ...

  9. 《Python计算机视觉编程》

    <Python计算机视觉编程> 基本信息 作者: (美)Jan Erik Solem 译者: 朱文涛 袁勇 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978711535 ...

随机推荐

  1. DeBug Python代码全靠print函数?换用这个一天2K+Star的工具吧,改进版

    pysnooper是代码debug神器,比无限low print好很多和也比日志debug好一些,比断点调试也好一些,这个很犀利的装饰器. https://www.toutiao.com/a66829 ...

  2. 【Dubbo 源码解析】06_Dubbo 服务调用

    Dubbo 服务调用 根据上图,可以看出,服务调用过程为: Consumer 端的 Proxy 调用 Cluster 层选择集群中的某一个 Invoker(负载均衡) Invoker 最终会调用 Pr ...

  3. MySQL设置密码复杂度

    MySQL5.6.6版本之后增加了密码强度验证插件validate_password,相关参数设置的较为严格.使用了该插件会检查设置的密码是否符合当前设置的强度规则,若不满足则拒绝设置. 本文采用测试 ...

  4. 【Static Program Analysis - Chapter 4】格理论(Lattice Theory)与程序分析

    # 从一个例子说起, **任务:给定这样一段代码,假设我们想分析出这段代码中,每个数值型变量和表达式的符号,即正数,负数或0.** 此外,还有可能出现两种情况就是: 1.我们无法分析出结果,即我们无法 ...

  5. [ICPC 北京 2017 J题]HihoCoder 1636 Pangu and Stones

    #1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...

  6. 案例源码解读及思路:RabbitMQ在springboot中的配置

    程序员的高级之处不是什么都会,而是对自己不会的进行抽象,然后完成自己的工作.比如对于RabbitMQ,按照字面理解,就将其看成Message Queue,也就是用来容纳对象的集合.很多功能都拆分给一个 ...

  7. 查看oracle数据库是否为归档模式

    查看oracle数据库是否为归档模式   [1]   1.select name,log_mode from v$database;   NAME LOG_MODE   --------------- ...

  8. 28. css样式中px转rem

    Vue3:脚手架配置 https://blog.csdn.net/weixin_41424247/article/details/80867351 与原来的vue-cli 2.x版本不同的是:如果使用 ...

  9. Java9之HashMap与ConcurrentHashMap

    HashMap在Java8之后就不再用link data bins了,而是转为用Treeify的bins,和之前相比,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成.: * This ...

  10. js设计模式(四)---迭代器模式

    定义: 迭代器模式是指提供一种方法,顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示,迭代器模式可以把迭代的过程从业务逻辑中分离出来,使用迭代器模式,即使不关心对象的内部构造,也可以按 ...