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

手部关键点检测的方法我之前已经详细写过,这里就直接使用,有不明白的可看我的这篇文章:【MediaPipe】(1) AI视觉,手部关键点实时跟踪,附python完整代码


1. 导入工具包

# 安装工具包
pip install opencv-contrib-python  # 安装opencv
pip install mediapipe  # 安装mediapipe
# pip install mediapipe --user  #有user报错的话试试这个
pip install cvzone  # 安装cvzone# 导入工具包
import cv2
from cvzone.HandTrackingModule import HandDetector
import mediapipe as mp
import time

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


2. 相关函数介绍

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

是已经编写好的检测方法,它的具体原理和我写的第一篇手部关键点检测的方法相同,想知道具体实现的方法可以去看一下。链接在文章开头。

参数:

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

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

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

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

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

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

参数:

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

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

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

返回值:绘制关键点后的img帧图像;以及21个关键点的坐标信息(列表形式)


3. 检测手部信息

这里设置 maxHand=1 最多检测一只手,我们只需要一只手点计算器就可以了。

使用 cv2.flip() 函数翻转读取的摄像机图像,因为我们自己的右边相当于摄像机的左边,需要统一一下,变成镜像关系。指定参数 flipCode=0 竖向翻转,flipCode=1 水平翻转

#(1)捕获摄像头
cap = cv2.VideoCapture(0)
cap.set(3, 1080)  # 显示框的宽1080
cap.set(4, 720)   # 显示框的高720pTime = 0  # 设置第一帧开始处理的起始时间# 手部检测方法,置信度为0.8,最多检测一只手
detector = HandDetector(detectionCon=0.8, maxHands=1)  #(2)处理每一帧图像
while True:# 接收图片是否导入成功、帧图像success, img = cap.read()# 翻转图像,保证摄像机画面和人的动作是镜像img = cv2.flip(img, flipCode=1)  #0竖直翻转,1水平翻转#(3)检测手部关键点,返回所有关键点的坐标和绘制后的图像hands, img = detector.findHands(img, flipType=False)# 查看FPScTime = time.time() #处理完一帧图像的时间fps = 1/(cTime-pTime)pTime = cTime  #重置起始时间# 在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小cv2.putText(img, str(int(fps)), (70,50), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)  # 显示图像,输入窗口名及图像数据cv2.imshow('image', img)    if cv2.waitKey(20) & 0xFF==27:  #每帧滞留20毫秒后消失,ESC键退出break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

效果图如下:


4. 创建虚拟计算器

接下来我们需要在屏幕上创建一个计算器界面,定义一个类方法Button。计算器一共有16个按键,我们在 buttonListvalues 中存放每个按键的文本。利用两个for循环,让16个按键都经过button类初始化,暂时不绘图,把按键信息存放到 buttonList 中。初始化方法是在while循环开始之前就已经进行了,button类方法中的绘图方法draw()是在读取每一帧图像时进行,通过for循环绘制buttonList列表中每个按键元素。

因此我们在上述代码中补充。

# 创建按键类
class Button:# 初始化,传入pos按键位置,每个矩形框的宽高,矩形框上的数字valuedef __init__(self, pos, width, height, value):  # 初始化在while循环之前完成self.pos = posself.width = widthself.height = heightself.value = value# 绘图方法在while循环之后完成def draw(self, img):# 绘制计算器轮廓,img画板,起点坐标,终点坐标,颜色填充cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height), (225,225,225), cv2.FILLED)# 给计算器添加边框cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height),(50,50,50), 3)# 按键添加文本,img画板,文本内容,坐标,字体,字体大小,字体颜色,线条宽度cv2.putText(img, self.value, (self.pos[0]+30,self.pos[1]+70),cv2.FONT_HERSHEY_COMPLEX, 2, (50,50,50), 2)#(1)捕获摄像头
cap = cv2.VideoCapture(0)
cap.set(3, 1280)  # 显示框的宽1280
cap.set(4, 720)   # 显示框的高720pTime = 0  # 设置第一帧开始处理的起始时间# ==1== 手部检测方法,置信度为0.8,最多检测一只手
detector = HandDetector(detectionCon=0.8, maxHands=1)  # ==2== 创建计算器按键
# 创建按钮内容列表
buttonListvalues = [['7', '8', '9', '*'],['4', '5', '6', '-'],['1', '2', '3', '+'],['0', '/', '.', '=']]buttonList = []  #存放每个按键的信息# 创建4*4个按键
for x in range(4): # 四列for y in range(4): # 四行xpos = x * 100 + 800  #得到四块宽为100的矩形的起点x坐标,从x=800开始ypos = y * 100 + 150  #起点y坐标# 传入起点坐标及宽高button1 = Button((xpos,ypos), 100, 100, buttonListvalues[y][x])buttonList.append(button1)  # 将确定坐标的矩形框信息存入列表中#(2)处理每一帧图像
while True:# 接收图片是否导入成功、帧图像success, img = cap.read()# 翻转图像,保证摄像机画面和人的动作是镜像img = cv2.flip(img, flipCode=1)  #0竖直翻转,1水平翻转#(3)检测手部关键点,返回所有绘制后的图像hands, img = detector.findHands(img, flipType=False)#(4)绘制计算器# 绘制计算器显示结果的部分,四个按键的宽合起来是400cv2.rectangle(img, (800, 50), (800+400, 70+100), (225,225,225), cv2.FILLED)# 结果框轮廓cv2.rectangle(img, (800, 50), (800+400, 70+100), (50,50,50), 3)# 遍历列表,调用类中的draw方法,绘制每个按键for button in buttonList:    button.draw(img)# 查看FPScTime = time.time() #处理完一帧图像的时间fps = 1/(cTime-pTime)pTime = cTime  #重置起始时间# 在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小cv2.putText(img, str(int(fps)), (70,50), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)  # 显示图像,输入窗口名及图像数据cv2.imshow('image', img)    if cv2.waitKey(20) & 0xFF==27:  #每帧滞留20毫秒后消失,ESC键退出break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

结果如下:


 5. 激活按钮完成数学计算

补充上述代码,从第(5)部分开始,detector.findDistance() 函数传入两个关键点的xy坐标和img画板,返回两点之间的长度length,绘制后的图像img。在这里主要研究食指,如果食指在某个按键框内,并且食指和中指的距离小于50,那么就认为是点击按键。

因此我们需要新建一个类方法 checkClick 来判断,食指和中指接触时,食指在哪个位置。依次遍历 buttonList 中的16个按键框信息,和当前食指所在位置(x,y),如果 (x, y) 在某个按键框内,即 x1 < x < x1 + width 且 y1 < y < y1 + height,那么就改变这个按键的颜色,表明已经点击,类方法 checkClick 返回True。因此,通过按键 buttonListvalues 的索引,我们就找到了我们点下去的是哪一个字符。

我们每帧图像点击按键,得到的字符串的各种组合,存放在 myEquation 变量中,如 '5 * 6 - 2'。当我们点击 '=' 号时,得到的应该是一个长的像数值的字符串,而不是数值表达式组成的字符串。这时,使用 eval() 函数,它会将长得像数值运算的字符串当作数值来计算,返回一个数值。再把它变成字符串文本显示出来就可以了。

由于图像每一帧的刷新的很快,可能我们点了一次2,就显示出来十几个2。因此我们需要设置一个延时器 delayCounter,只有当它为0时我们才能再点击,避免出现每一帧我们都点击按键,这里设置为50帧,每50帧点击一次。if delayCounter > 50: delayCounter = 0

如果想清空结果框怎么办呢,方法如下,只要在英文模式下点击键盘上的c键就可以了,key = cv2.waitKey(1); if key == ord('c'): myEquation = ' '

import cv2
from cvzone.HandTrackingModule import HandDetector
import mediapipe as mp
import time# 创建按键类
class Button:# 初始化,传入pos按键位置,每个矩形框的宽高,矩形框上的数字valuedef __init__(self, pos, width, height, value):  # 初始化在while循环之前完成self.pos = posself.width = widthself.height = heightself.value = value# 绘图方法在while循环之后完成def draw(self, img):# 绘制计算器轮廓,img画板,起点坐标,终点坐标,颜色填充cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height), (225,225,225), cv2.FILLED)# 给计算器添加边框cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height),(50,50,50), 3)# 按键添加文本,img画板,文本内容,坐标,字体,字体大小,字体颜色,线条宽度cv2.putText(img, self.value, (self.pos[0]+30,self.pos[1]+70),cv2.FONT_HERSHEY_COMPLEX, 2, (50,50,50), 2)# 点击按钮def checkClick(self, x, y): #传入食指尖坐标# 检查食指x坐标在哪一个按钮框内,x1 < x < x1 + width ,控制一列# 检查食指y坐标在哪一个按钮框内,y1 < y < y1 + height ,控制一行if self.pos[0] < x < self.pos[0] + self.width and \self.pos[1] < y < self.pos[1] + self.height:  # '\'用来换行# 如果点击按钮就改变按钮颜色cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height), (0,255,0), cv2.FILLED)# 边框还是原来的不变cv2.rectangle(img, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height),(50,50,50), 3)# 按键文本变颜色,面积变化cv2.putText(img, self.value, (self.pos[0]+30, self.pos[1]+70),cv2.FONT_HERSHEY_COMPLEX, 2, (0,0,255), 5)# 如果成功点击按钮就返回Truereturn Trueelse:return False#(1)捕获摄像头
cap = cv2.VideoCapture(0)
cap.set(3, 1280)  # 显示框的宽1280
cap.set(4, 720)   # 显示框的高720pTime = 0  # 设置第一帧开始处理的起始时间# ==1== 手部检测方法,置信度为0.8,最多检测一只手
detector = HandDetector(detectionCon=0.8, maxHands=1)  # ==2== 创建计算器按键
# 创建按钮内容列表
buttonListvalues = [['7', '8', '9', '*'],['4', '5', '6', '-'],['1', '2', '3', '+'],['0', '/', '.', '=']]buttonList = []  #存放每个按键的信息# 创建4*4个按键
for x in range(4): # 四列for y in range(4): # 四行xpos = x * 100 + 800  #得到四块宽为100的矩形的起点x坐标,从x=800开始ypos = y * 100 + 150  #起点y坐标# 传入起点坐标及宽高button1 = Button((xpos,ypos), 100, 100, buttonListvalues[y][x])buttonList.append(button1)  # 将确定坐标的矩形框信息存入列表中# ==3== 初始化结果显示框
myEquation = ''
# eval('5'+'5') ==> 10,eval()函数将数字字符串转换成数字计算
delayCounter = 0  #添加计数器,一次点击触发一次按钮,避免重复#(2)处理每一帧图像
while True:# 接收图片是否导入成功、帧图像success, img = cap.read()# 翻转图像,保证摄像机画面和人的动作是镜像img = cv2.flip(img, flipCode=1)  #0竖直翻转,1水平翻转#(3)检测手部关键点,返回所有绘制后的图像hands, img = detector.findHands(img, flipType=False)#(4)绘制计算器# 绘制计算器显示结果的部分,四个按键的宽合起来是400cv2.rectangle(img, (800, 50), (800+400, 70+100), (225,225,225), cv2.FILLED)# 结果框轮廓cv2.rectangle(img, (800, 50), (800+400, 70+100), (50,50,50), 3)# 遍历列表,调用类中的draw方法,绘制每个按键for button in buttonList:    button.draw(img)#(5)检测手按了哪个键if hands:  #如果手部关键点返回的列表不为空,证明检测到了手# 0代表第一只手,由于我们设置了只检测一只手,所以0就代表检测到的那只lmlist = hands[0]['lmList'] # 获取食指和中指的指尖距离并绘制连线# 返回指尖连线长度,线条信息,绘制后的图像length, _, img = detector.findDistance(lmlist[8], lmlist[12], img)# print(length)x, y = lmlist[8] # 获取食指坐标# 如果指尖距离小于50,找到按下了哪个键if length < 50: for i, button in enumerate(buttonList):  # 遍历所有按键,找到食指尖在哪个按键内# 点击按键,按键颜色面积发生变化,返回True。并且延时器为0才能运行if button.checkClick(x,y) and delayCounter==0:  #(6)数值计算# 找到点击的按钮的编号i,i是0-15,# 如"4",索引为4,位置[1][0],等同于[i%4][i//4]# print(buttonListvalues[i%4][i//4])myValue = buttonListvalues[i%4][i//4]# 如果点的是'='号if myValue == '=':# eval()使字符串数字和符号直接做计算, eval('5 * 6 - 2')myEquation = str(eval(myEquation))  #eval返回一个数值else:# 第一次点击"5",第二次点击"6",需要显示的是56myEquation += myValue  # 字符串直接相加# 避免重复,方法一,不推荐: # time.sleep(0.2)delayCounter = 1  # 启动计数器,一次运行点击了一个键#(7)避免点一次出现多个相同数,方法二:# 点击一个按钮之后,delayCounter=1,20帧后才能点击下一个if delayCounter != 0:delayCounter += 1 # 延迟一帧if delayCounter > 50:  # 10帧过去了才能再点击delayCounter = 0#(8)绘制显示的计算表达式cv2.putText(img, myEquation, (800+10,100+20), cv2.FONT_HERSHEY_PLAIN,3, (50,50,50), 3)# 查看FPScTime = time.time() #处理完一帧图像的时间fps = 1/(cTime-pTime)pTime = cTime  #重置起始时间# 在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小cv2.putText(img, str(int(fps)), (70,50), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)  # 显示图像,输入窗口名及图像数据cv2.imshow('image', img)# 每帧滞留时间key = cv2.waitKey(1)# 清空计算器框if key == ord('c'):myEquation = ''# 退出显示if key & 0xFF==27:  #每帧滞留20毫秒后消失,ESC键退出break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

没点击按钮时:

点击按钮时:

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

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

    各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成远程手势控制电脑键盘.感兴趣的可以看一下我前面一篇手势控制电脑鼠标:https://blog.csdn.net/dgvv4 ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. 【图像分类案例】(1) ResNeXt 交通标志四分类,附Tensorflow完整代码

    各位同学好,今天和大家分享一下如何使用 Tensorflow 构建 ResNeXt 神经网络模型,通过案例实战 ResNeXt 的训练以及预测过程.每个小节的末尾有网络.训练.预测的完整代码.想要数据 ...

  9. TensorFlow 实战案例:利用 LSTM、GRU 进行股票数据预测(附 Python 完整代码)

    大家好,今天和各位分享一下如何使用循环神经网络 LSTM 和 GRU 完成对股票数据的预测.GRU 是在 LSTM 基础上的简化,将 LSTM 内部的三个闸门简化成两个,往往 GRU 的计算效果会优于 ...

最新文章

  1. 二、多并发实现接口压力测试
  2. AutoFac Ioc依赖注入容器
  3. 边缘检测算法及各自优缺点
  4. win10 ObservableCollection 排序自动收缩问题
  5. 性能测试如何定位瓶颈?偶发超时?看高手如何快速排查问题
  6. facebook对话链接_并非里程碑! Facebook的100种语言互译模型夸大宣传遭质疑
  7. ubuntu18.04安装openresty
  8. 利用Helm简化Kubernetes应用部署(1)
  9. asp.net 安装element ui_不用上官网,自己部署一套Element官方最新文档
  10. 奇异值分解(SVD) 的 几何意义
  11. Cesium笔记(3):基本控件简介—ImageryProvider地图瓦片地图配
  12. win10 共享文件夹 nplayer查看共享文件
  13. 佳能Canon MF9200 Series 一体机驱动
  14. python+opencv+图像特效(图像灰度处理、颜色翻转、图片融合,边缘检测,浮雕效果,颜色映射)
  15. 爬取王者荣耀高清皮肤
  16. 中国传媒大学计算机与网络安全学院研究生,林卫国 - 中国传媒大学 - 计算机与网络空间安全学院...
  17. 微信小程序详细图文教程-10分钟完成微信小程序开发部署发布 小程序趟过的坑,你遇到几个??
  18. 《飞机大战小游戏(Java)》
  19. vue 海康视频播放
  20. Java微信扫码支付

热门文章

  1. 二逼平衡树——树套树(线段树套Splay平衡树)
  2. 修改或隐藏Nginx的版本号
  3. ios的并发队列控制库
  4. 5 -- Hibernate的基本用法 --2 1 Hibernate 下载和安装
  5. 接口与抽象类的使用选择
  6. delphi 10 seattle 中 解决IOS 9 限制使用HTTP 服务问题
  7. Await, and UI, and deadlocks! Oh my!
  8. 【HDU】3308 LCIS
  9. C#WinForm的线程及Invoke应用(转)
  10. vc2010解决方案项目编译顺序_科学网—VS2012 (2008,2010) 编译问题解决合集 - 冯博远的博文...