各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成远程手势控制电脑键盘。感兴趣的可以看一下我前面一篇手势控制电脑鼠标:https://blog.csdn.net/dgvv4/article/details/122268203?spm=1001.2014.3001.5501, 把这两个结合起来去打游戏会不会很有意思呢。先放张图看效果。

这里用百度搜索栏做测试,搜索框中的内容和opencv显示窗口上的内容是同步打印出来的。

工作原理:如果检测到食指指尖关键点坐标在某个按键框的范围内部,那么该按键显示绿色;如果食指指尖和中指指尖之间的距离小于规定距离,就认为是点击食指指尖所在的按键,按键变为红色;设置1秒内同一个按键只能点击一次,避免重复出现多个相同按键值。


1. 安装工具包

# 安装工具包
pip install opencv-contrib-python  # 安装opencv
pip install mediapipe  # 安装mediapipe
# pip install mediapipe --user  #有user报错的话试试这个
pip install cvzone  # 安装cvzone
pip install pynput  # 键盘控制单元# 导入工具包
import numpy as np
import cv2
from cvzone.HandTrackingModule import HandDetector  # 手部追踪方法
import mediapipe as mp
import time
from pynput.keyboard import Controller  # 键盘控制模块

21个手部关键点信息如下,本节我们主要研究食指指尖"8"中指指尖"12"的坐标信息。


2. 手部关键点检测,制作虚拟键盘

2.1 手部关键点检测

(1) cvzone.HandTrackingModule.HandDetector()   手部关键点检测方法

参数:

mode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有 maxHands 手并定位了相应的手的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一只手的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行手部检测,用于处理一批静态的、可能不相关的图像。

maxHands: 最多检测几只手,默认为 2

detectionCon: 手部检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5

minTrackingCon: 坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 mode 为 True,则忽略这个参数,手部检测将在每个图像上运行。默认为 0.5

它的参数和返回值类似于官方函数 mediapipe.solutions.hands.Hands()

(2)cvzone.HandTrackingModule.HandDetector.findHands()    找到手部关键点并绘图

参数:

img: 需要检测关键点的帧图像,格式为BGR

draw: 是否需要在原图像上绘制关键点及识别框

flipType: 图像是否需要翻转,当视频图像和我们自己不是镜像关系时,设为True就可以了

返回值:

hands: 检测到的手部信息,包含:21个关键点坐标,检测框坐标及宽高,检测框中心坐标,检测出是哪一只手。

img: 返回绘制了关键点及连线后的图像


2.2 绘制虚拟键盘

本节只创建键盘上的部分按键用于演示,定义类Button不需要一个一个单独绘制矩形按键。使用一个循环,分别对每个按键实例化将实例化结果保存在 buttonList 列表中。在第(6)步绘制键盘时,逐个取出实例化对象,在窗口上绘制30个按键。

代码如下:

import cv2
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块#(1)捕捉电脑摄像头
cap = cv2.VideoCapture(0) # 0代表自己的电脑摄像头,1代表外接摄像头
cap.set(3, 1280) # 设置显示框的宽1280
cap.set(4, 720)  # 设置显示框的高720#(2)接收手部检测方法
detector = HandDetector(mode=False,  # 视频流图像 maxHands=1,  # 最多检测一只手detectionCon=0.5,  # 最小检测置信度minTrackCon=0.5)   # 最小跟踪置信度#(3)创建一个类用于构造键盘按键
class Button:# 初始化,按键的左上坐标pos(列表类型),文本信息text(字符串类型),按键的宽高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配属性self.pos = posself.text = textself.size = size# 在类的内部定义方法def draw(self, img):x1, y1 = self.pos  # 矩形框的左上角坐标w, h = self.size   # 矩形框的宽高# img画板,矩形框左上角坐标,矩形框右下角坐标,颜色,-1代表颜色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), (255,0,0), -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), (255,255,0), 4)# 在矩形框上显示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 创建一个列表存放键盘上每个按键的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 存放每一个按键的信息
buttonList = []# 通过循环来实例化所有按键
for i in range(3):  # 键盘文本列表有三行# 分别实例化每一行的按钮信息 for x, key in enumerate(keys[i]):  #返回每个元素的索引和值# 确定每个按键的左上角坐标位置px = 115*x + 30 + 40*i  #水平方向每两个按键之间间隔115,每次换行缩进40,初始位置x=30py = 115*i + 50  # 竖直方向每两个按键之间间隔115,初始位置y=50# 将实例化后的对象存放在列表中buttonList.append(Button([px, py], key))# 每次换行后px坐标重置x = 0#(4)处理每一帧图像
while True:# 返回是否读取成功,和读取的帧图像success, img = cap.read()# 翻转图像,让自己和摄像头呈镜像关系img = cv2.flip(img, 1)  # 1代表水平翻转,0代表上下翻转#(5)检测手部关键点# 检测手部关键点信息,返回手部信息hands,绘制关键点后的图像imghands, img = detector.findHands(img, flipType=False)#(6)绘制键盘for i in range(3*len(keys[0])):  # 一共有3行10列个按键# 调用类中的绘图方法,显示每个键盘按键buttonList[i].draw(img)#(7)显示图像cv2.imshow('video', img)# 每帧图像滞留时间,ESC键退出if cv2.waitKey(1) & 0xFF==27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

手部检测结果及虚拟键盘如下图所示。


3. 锁定按键位置

从下面代码的第(6)步开始是锁定键盘按键的位置。通过 detector.findHands() 返回手部检测信息hands列表,由0或1或2个字典组成,如果检测到一只手就返回一个字典。字典中包含:lmList 代表21个手部关键点的像素坐标;bbox 代表检测框的左上角坐标和框的宽高;center 代表检测框的中心点的像素坐标;type 代表检测出的是左手还是右手。

只需要知道食指指尖坐标 lmList[8] 在哪个按键的范围内,并计算食指指尖和中指指尖之间的距离,距离小于某个值认为是点击按键。距离计算方法,detector.findDistance(pt1, pt2, img) 传入两个关键点坐标。返回值distance 代表两个关键点之间的距离, info 代表指尖连线的起点、中点、终点, img 代表绘制指尖连线后的图像。

在上述代码中补充:

import cv2
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块#(1)捕捉电脑摄像头
cap = cv2.VideoCapture(0) # 0代表自己的电脑摄像头,1代表外接摄像头
cap.set(3, 1280) # 设置显示框的宽1280
cap.set(4, 720)  # 设置显示框的高720#(2)接收手部检测方法
detector = HandDetector(mode=False,  # 视频流图像 maxHands=1,  # 最多检测一只手detectionCon=0.5,  # 最小检测置信度minTrackCon=0.5)   # 最小跟踪置信度#(3)创建一个类用于构造键盘按键
class Button:# 初始化,按键的左上坐标pos(列表类型),文本信息text(字符串类型),按键的宽高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配属性self.pos = posself.text = textself.size = size# 在类的内部定义方法,默认内部深蓝色填充,边框为浅蓝色def draw(self, img, colorIn, colorBd):x1, y1 = self.pos  # 矩形框的左上角坐标w, h = self.size   # 矩形框的宽高# img画板,矩形框左上角坐标,矩形框右下角坐标,颜色,-1代表颜色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorIn, -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorBd, 4)# 在矩形框上显示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 创建一个列表存放键盘上每个按键的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 存放每一个按键的信息
buttonList = []# 矩形按键的颜色
colorIn = (255,0,0)  # 按键内部填充的颜色
colorBd = (255,255,0)  # 按键的边框颜色            # 通过循环来实例化所有按键
for i in range(3):  # 键盘文本列表有三行# 分别实例化每一行的按钮信息 for x, key in enumerate(keys[i]):  #返回每个元素的索引和值# 确定每个按键的左上角坐标位置px = 115*x + 30 + 40*i  #水平方向每两个按键之间间隔115,每次换行缩进40,初始位置x=30py = 115*i + 50  # 竖直方向每两个按键指尖间隔115,初始位置y=50# 将实例化后的对象存放在列表中buttonList.append(Button([px, py], key))# 每次换行后px坐标重置x = 0#(4)处理每一帧图像
while True:# 返回是否读取成功,和读取的帧图像success, img = cap.read()# 翻转图像,让自己和摄像头呈镜像关系img = cv2.flip(img, 1)  # 1代表水平翻转,0代表上下翻转#(5)绘制键盘for i in range(3*len(keys[0])):  # 一共有3行10列个按键# 调用类中的绘图方法,显示每个键盘按键buttonList[i].draw(img, colorIn, colorBd)#(6)检测手部关键点# 检测手部关键点信息,返回手部信息hands,绘制关键点后的图像imghands, img = detector.findHands(img, flipType=False)# 如果检测到手了,才执行下一步if hands:# 获取21个手部关键点信息lmList = hands[0]['lmList']# 获取食指指尖关键点坐标x1, y1 = lmList[8]# 获取中指指尖关键点坐标x2, y2 = lmList[12]#(7)遍历所有的按键,检查食指指尖在哪个按键的范围内for index, button in enumerate(buttonList): # botton存放的是类实例化后的对象# 所在矩形的左上坐标和宽高x0, y0 = button.posw, h = button.size# 如果食指指尖在某个矩形框中,改变键盘按键颜色if x0<=x1<=x0+w and y0<=y1<=y0+h:# 按键内部填充颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,255,0), -1)# 按键边框颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), 4)# 按键上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(8)计算食指和中指指尖之间的距离distance, _, img = detector.findDistance((x1,y1), (x2,y2), img)# 如果指尖距离小于80,认为是点击按键if distance<50:# 点击按键改变按键颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), -1)# 按键上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(9)显示图像cv2.imshow('video', img)# 每帧图像滞留时间,ESC键退出if cv2.waitKey(1) & 0xFF==27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

显示结果如下,如果食指在某个按键矩形中并且食指和中指之间的距离大于规定值,那么这个按键填充绿色,边界框变红色食指在某个按键矩形中并且食指和中指之间的距离小于规定值,那么这个按键和边界框都填充红色


4. 激活键盘按键

下面的第(9)步,为了验证点击按键时 opencv 画面上的点击内容和百度搜索框的输入内容是否同步,在opencv画面上创建一个矩形框用来显示字符。

通过 keyboard.press() 获得键盘响应,输入值是键盘上的某个字符,表示点击键盘上的该字符。

由于每一帧播放的非常快,可能只点击了一次按键,却打印出来很多相同的字符。使用休眠函数 time.sleep(t) 每点击一次按键就暂停程序的执行,暂停 t 秒时间

import cv2
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块
from time import sleep
from pynput.keyboard import Controller  # 键盘控制单元#(1)捕捉电脑摄像头
cap = cv2.VideoCapture(0) # 0代表自己的电脑摄像头,1代表外接摄像头
cap.set(3, 1280) # 设置显示框的宽1280
cap.set(4, 720)  # 设置显示框的高720#(2)接收手部检测方法
detector = HandDetector(mode=False,  # 视频流图像 maxHands=1,  # 最多检测一只手detectionCon=0.5,  # 最小检测置信度minTrackCon=0.5)   # 最小跟踪置信度# 接收键盘控制单元
keyboard = Controller()#(3)创建一个类用于构造键盘按键
class Button:# 初始化,按键的左上坐标pos(列表类型),文本信息text(字符串类型),按键的宽高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配属性self.pos = posself.text = textself.size = size# 在类的内部定义方法,默认内部深蓝色填充,边框为浅蓝色def draw(self, img, colorIn, colorBd):x1, y1 = self.pos  # 矩形框的左上角坐标w, h = self.size   # 矩形框的宽高# img画板,矩形框左上角坐标,矩形框右下角坐标,颜色,-1代表颜色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorIn, -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorBd, 4)# 在矩形框上显示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 创建一个列表存放键盘上每个按键的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 保存最终的输出结果
finalText = ''# 存放每一个按键的信息
buttonList = []# 矩形按键的颜色
colorIn = (255,0,0)  # 按键内部填充的颜色
colorBd = (255,255,0)  # 按键的边框颜色            # 通过循环来实例化所有按键
for i in range(3):  # 键盘文本列表有三行# 分别实例化每一行的按钮信息 for x, key in enumerate(keys[i]):  #返回每个元素的索引和值# 确定每个按键的左上角坐标位置px = 115*x + 30 + 40*i  #水平方向每两个按键之间间隔115,每次换行缩进40,初始位置x=30py = 115*i + 50  # 竖直方向每两个按键指尖间隔115,初始位置y=50# 将实例化后的对象存放在列表中buttonList.append(Button([px, py], key))# 每次换行后px坐标重置x = 0#(4)处理每一帧图像
while True:# 返回是否读取成功,和读取的帧图像success, img = cap.read()# 翻转图像,让自己和摄像头呈镜像关系img = cv2.flip(img, 1)  # 1代表水平翻转,0代表上下翻转#(5)绘制键盘for i in range(3*len(keys[0])):  # 一共有3行10列个按键# 调用类中的绘图方法,显示每个键盘按键buttonList[i].draw(img, colorIn, colorBd)#(6)检测手部关键点# 检测手部关键点信息,返回手部信息hands,绘制关键点后的图像imghands, img = detector.findHands(img, flipType=False)# 如果检测到手了,才执行下一步if hands:# 获取21个手部关键点信息lmList = hands[0]['lmList']# 获取食指指尖关键点坐标x1, y1 = lmList[8]# 获取中指指尖关键点坐标x2, y2 = lmList[12]#(7)遍历所有的按键,检查食指指尖在哪个按键的范围内for index, button in enumerate(buttonList): # botton存放的是类实例化后的对象# 所在矩形的左上坐标和宽高x0, y0 = button.posw, h = button.size# 如果食指指尖在某个矩形框中,改变键盘按键颜色if x0<=x1<=x0+w and y0<=y1<=y0+h:# 按键内部填充颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,255,0), -1)# 按键边框颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), 4)# 按键上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(8)计算食指和中指指尖之间的距离distance, _, img = detector.findDistance((x1,y1), (x2,y2), img)# 如果指尖距离小于50,认为是点击按键if distance<50:# 点击按键改变按键颜色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), -1)# 按键上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 点击键盘上某个按键keyboard.press(button.text) # 键盘上的某个符号,'A'# 在文本框中显示该字符finalText += button.text# 点击一次后,0.2秒之后才能再点一次sleep(0.2)#(9)创建虚拟文本框# 文本框内部cv2.rectangle(img, (100, 450), (700, 550), (255,255,255), -1)# 文本框边框cv2.rectangle(img, (100, 450), (700, 550), (0,0,0), 5)    # 按键上的字符串cv2.putText(img, finalText, (110, 525), cv2.FONT_HERSHEY_COMPLEX, 1.8, (0,0,0), 3)#(10)显示图像cv2.imshow('video', img)# 每帧图像滞留时间,ESC键退出if cv2.waitKey(1) & 0xFF==27:break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

显示结果如图,当食指在某个按键的范围内,并且指尖距离大于规定值,认为是搜索按键,按键变成绿色,边界框变成红色;如果指尖距离小于规定值,认为是点击按键,按键内部和边框都变成红色。使用百度搜索框测试,能够实现同步输入。

【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码相关推荐

  1. 【opencv】(13) 案例:停车场空余车位检测,附python完整代码

    各位同学好,今天和大家分享一下如何使用Opencv完成停车场的车位检测,及空余车位计数,先放张图看效果. 红框代表该车位有车,绿框代表该车位空余,左上角记录有几个空余车位,黄色数字代表该车位内的像素个 ...

  2. 【机器视觉案例】(5) AI视觉,远程手势控制虚拟计算器,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe+Opencv完成虚拟计算器,先放张图看效果.FPS值为29,食指和中指距离小于规定阈值则认为点击按键,为避免重复数字出现,规定每20帧可点击一 ...

  3. 【机器视觉案例】(5) AI视觉,手势调节物体尺寸,附python完整代码

    各位同学好,今天和大家分享一下如何使用opencv+mediapipe完成远程手势调节图片尺寸的案例.先放张图看效果.当拇指和食指竖起时,根据食指间的连线的长度自由缩放图片尺寸.图片的中点始终位于指尖 ...

  4. 【MediaPipe】(4) AI视觉,远程手势调节电脑音量,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe完成手势调节电脑音量,先放张图看效果. 注意!! 本节需要用到手部关键点的实时跟踪,我已经在之前的文章中详细写过了,本节会直接使用,有疑问的同学 ...

  5. 【机器视觉案例】(6) AI视觉,距离测量,自制AI小游戏,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv + mediapipe 创建一个AI视觉小游戏,先放图看效果. 游戏规则,用手按下屏幕上的圆形按钮,每按一次后松开,按钮就随机出现在屏幕上的一个位 ...

  6. 【机器视觉案例】(8) AI视觉,手势控制电脑鼠标,附python完整代码

    各位同学好,今天和大家分享一下如何使用 MediaPipe+Opencv 通过手势识别来控制电脑鼠标的移动和点击,如果有兴趣的话,可以代替鼠标去打游戏.先放图看效果.用画图板来测试 黄框代表电脑屏幕的 ...

  7. 【机器视觉案例】(10) AI视觉搭积木,手势移动虚拟物体,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成手势移动虚拟物体,可自定义各种形状的物体,通过手势搭积木.先放张图看效果. 规则:当食指在某个物体内部,并且中指指尖和食指 ...

  8. 【机器视觉案例】(12) 自制AI视觉小游戏--贪吃蛇,附python完整代码

    各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 自制贪吃蛇小游戏.先放张图看效果. 规则:食指指尖控制蛇头,指尖每接触到黄色方块,计数加一,蛇身变长,方块随机切换位置.如果指 ...

  9. 【机器视觉案例】(16) 自制视觉小游戏--桌上冰球,附python完整代码

    大家好,今天和各位分享一下如何使用 mediapipe+opencv 制作桌上冰球的交互式小游戏.先放张图看效果. 规则如下:左手控制白色球拍:右手控制紫色球拍:球拍只能上下移动:红色圆形就是冰球:球 ...

最新文章

  1. 京津冀计算机学科大学排名,2021京津冀地区大学排名!北师大第3,北航第7,人大不敌天大?...
  2. xmemcached发布1.3.6
  3. 想要求职Web安全相关的岗位,你就必须要懂的知识
  4. 21天学通HTML5和CSS3第二版,21天学通HTML5+CSS3
  5. java font是什么意思_font-family of a font,它是什么?
  6. 机器学习数据倾斜的解决方法_机器学习并不总是解决数据问题的方法
  7. Android中调用系统已安装的播放器来播放网络流媒体视频
  8. Java Web的web.xml文件作用及基本配置(转)
  9. 洛谷4014最大/小费用最大流
  10. JavaScript中数组去重汇总
  11. Insurance 项目——Mybetis-generator生成
  12. 数学建模国赛论文latex代码汇总
  13. 安卓socket客户端
  14. KEPServer EX6的Modbus、MQTT和REST Server一站式配置整理说明
  15. 市场调研-全球与中国LED多层指示灯市场现状及未来发展趋势
  16. Mr. Kitayuta vs. Bamboos[二分+贪心][图像分析]
  17. Java8新特性之Optional类(附代码案例)
  18. 拼多多的“最初一公里”战事
  19. 第14节:lstm原理及numpy
  20. python dataframe dropna_在Python中使用熊猫在两个DataFrame之间进行值...

热门文章

  1. Installation error: INSTALL_FAILED_VERSION_DOWNGRADE
  2. RecyclerView 点击Item 改变文字颜色以及所在的背景色
  3. +load +initialize
  4. Python学习笔记3——三大结构:顺序,分支,循环3
  5. 深入理解JVM(二)--垃圾收集算法
  6. 一个较为详细的ETL系统实现方案
  7. 写《回国驯火记》的那个安普若
  8. 笔记本电脑摄像头实现光流跟踪
  9. php codeigniter 语言,php – codeigniter模板引擎,包括语言解析器
  10. java做个简单的登录界面_java一个简单的登录界面制作