Python手势识别
这是借鉴了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 键就可以捕获背景了
运行结果:
注:模拟点击空格键部分并未展示出来,有兴趣的可以尝试一下(按下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手势识别的更多相关文章
- Python手势识别与控制
代码地址如下:http://www.demodashi.com/demo/12968.html Python手势识别与控制 概述 本文中的手势识别与控制功能主要采用 OpenCV 库实现, OpenC ...
- Python C++ OpenCV TensorFlow手势识别(1-10) 毕设 定制开发
Python C++ OpenCV TensorFlow手势识别(1-10) 毕设 支持定制开发 (MFC,QT, PyQt5界面,视频摄像头识别) QQ: 3252314061 效果如下:
- Python爬取CSDN博客文章
0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...
- 【NLP】3000篇搜狐新闻语料数据预处理器的python实现
3000篇搜狐新闻语料数据预处理器的python实现 白宁超 2017年5月5日17:20:04 摘要: 关于自然语言处理模型训练亦或是数据挖掘.文本处理等等,均离不开数据清洗,数据预处理的工作.这里 ...
- 基于ssd的手势识别模型(object detection api方式)
[Tensorflow]Object Detection API-训练自己的手势识别模型 1. 安装tensorflow以及下载object detection api 1.安装tensorflow: ...
- 超声波手势识别(STM32四路超声波获取)
超声波手势识别在市场上已经有见实现,但研究其传感器发现并不是市场上随意可见的,如果暂且考虑成本,该如何入门实现简单的手势识别呢.聊天中老师给出一个很好的提议,就是固定四个超声波,分别为上下左右,然后进 ...
- Python中调用自然语言处理工具HanLP手记
手记实用系列文章: 1 结巴分词和自然语言处理HanLP处理手记 2 Python中文语料批量预处理手记 3 自然语言处理手记 4 Python中调用自然语言处理工具HanLP手记 5 Python中 ...
- hanlp自然语言处理包的基本使用--python
hanlp拥有:中文分词.命名实体识别.摘要关键字.依存句法分析.简繁拼音转换.智能推荐. 这里主要介绍一下hanlp的中文分词.命名实体识别.依存句法分析,这里就不介绍具体的hanlp的安装了,百度 ...
- 《Python计算机视觉编程》
<Python计算机视觉编程> 基本信息 作者: (美)Jan Erik Solem 译者: 朱文涛 袁勇 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978711535 ...
随机推荐
- TIJ -- 任务间使用管道进行输入/输出
1. 通过输入/输出在线程间进行通信通常很有用.提供线程功能的类库以“管道”的形式对线程间的输入/输出提供了支持.它们在Java输入/输出类库中的对应物就是PipedWriter类(允许任务向管道写) ...
- Xshell正编辑文件时掉线,需再次正常编辑解决办法
E325: ATTENTION Found a swap file by the name ".weather.py.swp" owned by: pi dated: Mon No ...
- cf 1132 F
区间dp.. 每次删一串相邻的一样的,问多少次删光. 感觉想的几乎是一样的怎么比赛时就过不了呢...一定是因为我挂机睡觉了 考虑l和r相等,l和l+1相等,r和r-1相等这三种情况就行了..然后就是裸 ...
- oracle数据库字符集查询
1>数据库服务器字符集 select * from nls_database_parameters,其来源于props$,是表示数据库的字符集. 查询结果如下 NLS_LANGUAGE AMER ...
- 【C++ 实验5 类和对象】
1. #include <iostream> #include <vector> #include <string> using namespace std; // ...
- AUC计算 - 手把手步进操作
2017-07-10 14:38:24 理论参考: 评估分类器性能的度量,像混淆矩阵.ROC.AUC等 http://www.cnblogs.com/suanec/p/5941630.html ROC ...
- 通俗bandit算法
[原文链接] 选择是一个技术活 著名鸡汤学家沃.滋基硕德曾说过:选择比努力重要. 我们会遇到很多选择的场景.上哪个大学,学什么专业,去哪家公司,中午吃什么,等等.这些事情,都让选择困难症的我们头很大. ...
- 对Http协议基本原理的理解
超文本传输协议 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准.设计HTTP最初的目的是 ...
- Java 递归获取一个路径下的所有文件,文件夹名称
package com.readfile; import java.io.File; public class GetAllFiles { public static void main(String ...
- Linux命令 umask
umask: 文件预设权限 指定当前用户在创建文件或目录时的权限默认值. $ umask0002$ umask -Su=rwx,g=rwx,o=rx 创建文件时,预设没有x 权限,即只有rw 权限,最 ...