Python手势识别与控制

概述

本文中的手势识别与控制功能主要采用 OpenCV 库实现, OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库, 可以运行在Linux, Windows, Android和Mac-OS操作系统上. 它轻量级而且高效—-由一系列 C 函数和少量 C++ 类构成, 同时提供了Python, Ruby, MATLAB等语言的接口, 实现了图像处理和计算机视觉方面的很多通用算法.

本文主要使用了OpenCV的视频采集, 图像色域转换, 颜色通道分割, 高斯滤波, OSTU自动阈值, 凸点检测, 边缘检测, 余弦定理计算手势等功能.

准备工作

安装 Python-OpenCV 库pip install opencv-python -i https://mirrors.ustc.edu.cn/pypi/web/simple

利用 -i 为pip指令镜像源, 这里使用电子科技大学的源, 速度比官方源更快.

安装 Numpy 科学计算库pip install numpy -i https://mirrors.ustc.edu.cn/pypi/web/simple

安装 PyAutogui 库pip install pyautogui -i https://mirrors.ustc.edu.cn/pypi/web/simple

图像的基本操作

import numpy as np

import cv2

imname = '6358772.jpg'

# 读入图像

'''

使用函数 cv2.imread() 读入图像。这幅图像应该在此程序的工作路径,或者给函数提供完整路径.

警告:就算图像的路径是错的,OpenCV 也不会提醒你的,但是当你使用命令print(img)时得到的结果是None。

'''

img = cv2.imread(imname, cv2.IMREAD_COLOR)

'''

imread函数的第一个参数是要打开的图像的名称(带路径)

第二个参数是告诉函数应该如何读取这幅图片. 其中

cv2.IMREAD_COLOR 表示读入一副彩色图像, alpha 通道被忽略, 默认值

cv2.IMREAD_ANYCOLOR 表示读入一副彩色图像

cv2.IMREAD_GRAYSCALE 表示读入一副灰度图像

cv2.IMREAD_UNCHANGED 表示读入一幅图像,并且包括图像的 alpha 通道

'''

# 显示图像

'''

使用函数 cv2.imshow() 显示图像。窗口会自动调整为图像大小。第一个参数是窗口的名字,

其次才是我们的图像。你可以创建多个窗口,只要你喜欢,但是必须给他们不同的名字.

'''

cv2.imshow('image', img) # 'image' 参数为图像显示窗口的标题, img是待显示的图像数据

cv2.waitKey(0) #等待键盘输入,参数表示等待时间,单位毫秒.0表示无限期等待

cv2.destroyAllWindows() # 销毁所有cv创建的窗口

# 也可以销毁指定窗口:

#cv2.destroyWindow('image') # 删除窗口标题为'image'的窗口

# 保存图像

'''

使用函数 cv2.imwrite() 来保存一个图像。首先需要一个文件名,之后才是你要保存的图像。

保存的图片的格式由后缀名决定.

'''

#cv2.imwrite(imname + '01.png', img)

cv2.imwrite(imname + '01.jpg', img)

摄像头数据采集

我们经常需要使用摄像头捕获实时图像。OpenCV 为这种应用提供了一个非常简单的接口。让我们使用摄像头来捕获一段视频,并把它转换成灰度视频显示出来。从这个简单的任务开始吧。

为了获取视频,你应该创建一个 VideoCapture 对象。它的参数可以是设备的索引号,或者是一个视频文件。设备索引号就是在指定要使用的摄像头。一般的笔记本电脑都有内置摄像头。所以参数就是 0。你可以通过设置成 1 或者其他的来选择别的摄像头。之后,你就可以一帧一帧的捕获视频了。但是最后,别忘了停止捕获视频。

cap.read() 返回一个布尔值(True/False)。如果帧读取的是正确的,就是 True。所以最后你可以通过检查他的返回值来查看视频文件是否已经到了结尾。有时 cap 可能不能成功的初始化摄像头设备。这种情况下上面的代码会报错。你可以使用cap.isOpened(),来检查是否成功初始化了。如果返回值是True,那就没有问题。否则就要使用函数 cap.open()。class Capture(object):

'''

Capture object

:param deviceID: device ID of your capture device, defaults to 0

:type deviceID: :obj:`int`

Example

>>> import pygr

>>> cap = pygr.Capture()

'''

def __init__(self, deviceID=0): # ID为0, 表示从默认的摄像头读取视频数据

self.deviceID = deviceID

self.capture = cv2.VideoCapture(self.deviceID) #

def read(self):

_, frame = self.capture.read() # 调用默认摄像头捕获一帧图像

frame = cv2.bilateralFilter(frame, 5, 50, 100) # 对捕获到的图像进行双边滤波

image = Image.fromarray(frame) # 转换图像数据格式

return image

视频数据的处理

为了更准确的识别视频数据中包含的手势信息, 需要对视频数据进行预处理, 包括背景减除, 人体皮肤侦测.

背景减除

在很多基础应用中背景检出都是一个非常重要的步骤。例如顾客统计,使用一个静态摄像头来记录进入和离开房间的人数,或者是交通摄像头,需要提取交通工具的信息等。在所有的这些例子中,首先要将人或车单独提取出来。

技术上来说,我们需要从静止的背景中提取移动的前景。如果你有一张背景(仅有背景不含前景)图像,比如没有顾客的房间,没有交通工具的道路等,那就好办了。我们只需要在新的图像中减去背景就可以得到前景对象了。

但是在大多数情况下,我们没有这样的(背景)图像,所以我们需要从我们有的图像中提取背景。如果图像中的交通工具还有影子的话,那这个工作就更难了,因为影子也在移动,仅仅使用减法会把影子也当成前景。真是一件很复杂的事情。为了实现这个目的科学家们已经提出了几种算法。OpenCV 中已经包含了其中三种比较容易使用的方法: BackgroundSubtractorMOG , BackgroundSubtractorMOG2 , BackgroundSubtractorGMG。这里我们使用的是 BackgroundSubtractorMOG2 .

BackgroundSubtractorMOG 和 BackgroundSubtractorMOG2

BackgroundSubtractorMOG2 是一个以混合高斯模型为基础的前景/背景分割算法。它是 P.KadewTraKuPong和 R.Bowden 在 2001 年提出的。它使用 K(K=3 或 5)个高斯分布混合对背景像素进行建模。使用这些颜色(在整个视频中)存在时间的长短作为混合的权重。背景的颜色一般持续的时间最长,而且更加静止。一个像素怎么会有分布呢?在 x,y 平面上一个像素就是一个像素没有分布,但是我们现在讲的背景建模是基于时间序列的,因此每一个像素点所在的位置在整个时间序列中就会有很多值,从而构成一个分布。

在编写代码时,我们需要使用函数:cv2.createBackgroundSubtractorMOG()创建一个背景对象。这个函数有些可选参数,比如要进行建模场景的时间长度,高斯混合成分的数量,阈值等。将他们全部设置为默认值。然后在整个视频中我们是需要使用 backgroundsubtractor.apply()就可以得到前景的掩模了。

BackgroundSubtractorMOG2也是以高斯混合模型为基础的背景/前景分割算法。它是以 2004 年和 2006 年 Z.Zivkovic 的两篇文章为基础的。这个算法的一个特点是它为每一个像素选择一个合适数目的高斯分布。(上一个方法中我们使用是 K 高斯分布)。这样就会对由于亮度等发生变化引起的场景变化产生更好的适应。

和前面一样我们需要创建一个背景对象。但在这里我们我们可以选择是否检测阴影。如果 detectShadows = True(默认值),它就会检测并将影子标记出来,但是这样做会降低处理速度。影子会被标记为灰色。

我们这里使用的就是 BackgroundSubtractorMOG2 算法, 详细代码如下:# 移除视频数据的背景噪声

def _remove_background(frame):

fgbg = cv2.createBackgroundSubtractorMOG2() # 利用BackgroundSubtractorMOG2算法消除背景

# fgmask = bgModel.apply(frame)

fgmask = fgbg.apply(frame)

# kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))

# res = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)

kernel = np.ones((3, 3), np.uint8)

fgmask = cv2.erode(fgmask, kernel, iterations=1)

res = cv2.bitwise_and(frame, frame, mask=fgmask)

return res

# 视频数据的人体皮肤检测

def _divskin_detetc(frame):

# 肤色检测: YCrCb之Cr分量 + OTSU二值化

ycrcb = cv2.cvtColor(frame, cv2.COLOR_BGR2YCrCb) # 分解为YUV图像,得到CR分量

(_, cr, _) = cv2.split(ycrcb)

cr1 = cv2.GaussianBlur(cr, (5, 5), 0) # 高斯滤波

_, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # OTSU图像二值化

return skin

手势检测与识别

利用opencv提供的 convexityDefects 凹点检测函数检测图像凹陷的点, 然后利用, 然后根据凹陷点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势.

其中,锐角个数为0 ,表示 手势是 拳头 或 一,

锐角个数为0 ,表示 手势是 拳头 或 一,

锐角个数为1 ,表示 手势是 剪刀

锐角个数为2 ,表示 手势是 三,

锐角个数为3 ,表示 手势是 四,

锐角个数为4 ,表示 手势是 布

凹陷点计算

对象上的任何凹陷都被称为凸缺陷。OpenCV 中有一个函数 cv.convexityDefect() 可以帮助我们找到凸缺

陷. 函数调用如下. 如果要查找凸缺陷,在使用函数 cv2.convexHull 找凸包时,参数returnPoints一定要是 False.hull = cv2.convexHull(cnt, returnPoints = False)

defects = cv2.convexityDefects(cnt,hull)

它会返回一个数组,其中每一行包含的值是 [起点,终点,最远的点,到最远点的近似距离]。# 检测图像中的凸点(手指)个数

def _get_contours(array):

# 利用findContours检测图像中的轮廓, 其中返回值contours包含了图像中所有轮廓的坐标点

_, contours, _ = cv2.findContours(array, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

return contours

# 根据图像中凹凸点中的 (开始点, 结束点, 远点)的坐标, 利用余弦定理计算两根手指之间的夹角, 其必为锐角, 根据锐角的个数判别手势.

def _get_defects_count(array, contour, defects, verbose = False):

ndefects = 0

for i in range(defects.shape[0]):

s,e,f,_ = defects[i,0]

beg = tuple(contour[s][0])

end = tuple(contour[e][0])

far = tuple(contour[f][0])

a = _get_eucledian_distance(beg, end)

b = _get_eucledian_distance(beg, far)

c = _get_eucledian_distance(end, far)

angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # * 57

if angle <= math.pi/2 :#90:

ndefects = ndefects + 1

if verbose:

cv2.circle(array, far, 3, _COLOR_RED, -1)

if verbose:

cv2.line(array, beg, end, _COLOR_RED, 1)

return array, ndefects

def grdetect(array, verbose = False):

event = Event(Event.NONE)

copy = array.copy()

array = _remove_background(array) # 移除背景, add by wnavy

thresh = _divskin_detetc(array)

contours = _get_contours(thresh.copy()) # 计算图像的轮廓

largecont = max(contours, key = lambda contour: cv2.contourArea(contour))

hull = cv2.convexHull(largecont, returnPoints = False) # 计算轮廓的凸点

defects = cv2.convexityDefects(largecont, hull) # 计算轮廓的凹点

if defects is not None:

# 利用凹陷点坐标, 根据余弦定理计算图像中锐角个数

copy, ndefects = _get_defects_count(copy, largecont, defects, verbose = verbose)

# 根据锐角个数判断手势, 会有一定的误差

if ndefects == 0:

event.setType(Event.ZERO)

elif ndefects == 1:

event.setType(Event.TWO)

elif ndefects == 2:

event.setType(Event.THREE)

elif ndefects == 3:

event.setType(Event.FOUR)

elif ndefects == 4:

event.setType(Event.FIVE)

return event

手势控制

只要能够检测到手势, 相应的控制就简单很多了, 这里主要模拟手势控制web页面滚动, 手势 五 表示向下滚动, 手势 四 表示向上滚动. 只要明白了手势识别的核心原理, 更复杂的手势控制就完全看个人想象, 比如网络上有很多人实现 手势控制chrome浏览器中的那只小恐龙, 我也尝试过, 但是控制起来难度太大了, 还有的是实现手势播放, 切换歌曲. 只要有python, 这一切都很简单.# imports - app imports

import pygr

import cv2

import time

from base import Keycode, Event

import pyautogui

if __name__ == '__main__':

pad = pygr.PyGR(size = (480, 320), verbose = True)

#pad = pygr.PyGR(size = (480, 320))

pad.show()

while cv2.waitKey(10) not in [Keycode.ESCAPE, Keycode.Q, Keycode.q]:

event = pad.get_event()

print('Event:', event.type, 'Tip:', event.tip)

if event.type == Event.FIVE : # 手势五, 向下滚动

pyautogui.scroll(-30) # scroll down 10 'clicks'

elif event.type == Event.FOUR :# 手势四, 向上滚动

pyautogui.scroll(30) # scroll up 10 'clicks'

time.sleep(0.1)

补充说明

为了demo简洁明了, 本文中省去了代码里的无关细节和调试部分, 比如程序运行时的实时识别和标记窗口部分的代码没有介绍, 但是代码中都有详细注释. 另外,对于不想研究细节只想 拿来主义 的同学, 只需要仿照上面 main中引入相关模块, 然后创建并运行PyGR模块, 不需要任何多余的操作.

运行方法python main.py

注意事项main.py中调用PYGR函数时传入 verbose = True 参数,不然不会显示调试框

背景尽量纯色,以提高识别准确率,手要放在红色方框区域内

就这两条,你们方便就加在开头吧。我在评论里说了的

程序运行截图

手势识别

手势控制

项目内文件截图

好了,今天就分享到这里,希望本文对大家有所帮助,如果大家觉得有用可以点个关注支持一下谢谢!

python手势识别控制幻灯片_手把手教你如何实现Python手势识别与控制(含代码及动图)...相关推荐

  1. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  2. 软件_手把手教vscode配置c++,python开发环境

    原创:软件_手把手教vscode配置c++,python开发环境 之前主用Python作为项目开发语言,将项目迁移到arm边缘盒子上后发现arm的cpu不给力,软件速度低于预期,所以计划将部分程序改为 ...

  3. python如何编游戏_手把手教你用python写游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,项目来自 ...

  4. python进行数据预测_手把手教你用Python进行时间序列分解和预测

    原标题:手把手教你用Python进行时间序列分解和预测 作者:Mohit Sharma 翻译:数据派THU-王闯(Chuck) 预测是一件复杂的事情,在这方面做得好的企业会在同行业中出类拔萃.时间序列 ...

  5. python 词云手把手_手把手教你生成炫酷的词云

    前言: 话说,在这个大数据时代,获取信息显得极为容易,可正是如此,我们想要对信息进行直观地了解难度就大了.Excel是一个很好的数据可视化方法,不过有时候我们的数据来源可能并不是一张或者多张Excel ...

  6. python爬虫如何连接数据库_手把手教你如何在Navicat中如何新建连接数据库及相关报错解决方法...

    前几天给大家分享了如何安装Navicat,没有来得及上车的小伙伴可以戳这篇文章:手把手教你安装Navicat--靠谱的Navicat安装教程.今天给大家分享一下Navicat的简单使用教程,具体的教程 ...

  7. python numpy安装教程_手把手教你搭建机器学习开发环境—Python与NumPy的超简安装教程...

    手把手教你搭建机器学习开发环境Python语言是机器学习的基础,所以,想要入门机器学习,配置好Python的开发环境是第一步.本文就手把手的教你配置好基于Python的机器学习开发环境.超简单!第一步 ...

  8. python自助电影售票机_手把手教你用python抢票回家过年(代码简单)

    首先看看如何快速查看剩余火车票? 当你想查询一下火车票信息的时候,你还在上12306官网吗?或是打开你手机里的APP?下面让我们来用Python写一个命令行版的火车票查看器, 只要在命令行敲一行命令就 ...

  9. 专业放心的python入门视频_手把手教你掌握学习Python方法,让你不再从入门到放弃...

    随着数据科学概念的普及,Python 这门并不算新的语言火得一塌糊涂.因为写了几篇用 Python 做数据分析的 文章,经常有读者和学生私信问我,想学习 Python,该如何入手?我经常需要根据学习, ...

最新文章

  1. CocosCreator TOUCH_MOVE事件
  2. BP神经网络 语音信号分类
  3. 【机器学习】搞懂机器学习的常用评价指标!
  4. 一个机械系毕业生的感言
  5. 2.4-2.5、Hive整合(整合Spark、整合Hbase)、连接方式Cli、HiveServer和hivemetastore、Squirrel SQL Client等
  6. CMake 进行调试
  7. 大端字节序码流中取出2字节_产生字节码
  8. 30天提升技术人的写作力-第十七天
  9. mac sierra 10.12部分注册机Special-K+CORE Keygen不能运行的问题
  10. Python Turtle画分形树理解递归
  11. fiddler4写插件总结
  12. 阿里云AIoT造物秘籍●开放下载
  13. 附录B. Design Motivations【设计意图】
  14. sql语句练习(1) 含问题,答案,数据库表,数据
  15. STM32cubeMX I2S DMA双缓冲配置
  16. 又要放大招了 监控中干扰如何消除你可知道?
  17. [编程范式]以炒菜为例,讲解各种编程范式
  18. 数据库表的映射与java类映射处理
  19. 对称密钥算法和公钥算法的优缺点
  20. objective c 函数声明

热门文章

  1. 修改转移表空间些许操作
  2. 会议室怎么添加预约功能?快速解决会议室使用效率问题
  3. chrome各历史版本下载地址汇总
  4. h5背景音乐 <music>
  5. 升级php到php7.4版本
  6. 架构解析 | 从ABTest是啥开始说
  7. 53、记录调试瑞芯微开发板以及失败记录
  8. RSVP协议的基本概念介绍
  9. 华为手机键盘android不能长按,输入法cherry经典机械键盘怎么不能长按出来数字...
  10. aris终端桌面_Aris-终端桌面(shinado.indi.piping) - 3.9.4 - 应用 - 酷安网