1.环境准备
本人采用jupyter notebook作为运行环境
1.1python解释器的安装
网盘链接:https://pan.baidu.com/s/1mDv5lYJbm9rJR-fUh1bE3A
提取码:phm6
1.2anaconda的安装
网盘链接:???????https://pan.baidu.com/s/1MyHOT7hGss0SXaGeeOx8LQ
提取码:phm6
1.3jupyter的安装
打开终端输入指令
pip install jupyter -i https://pypi.tuna.tsinghua.edu.cn/simple
到这里环境大致已经搭建完成,只需要进入终端输入指令
jupyter notebook
就可以运行jupyter了,在那里启动,浏览器上的目录对应那里,为了方便建议建立一个文件夹作为jupyter notebook的工作目录,每次运行时在终端cd进入工作目录,然后再运行jupyter
1.4notebook拓展插件的安装
安装相应的拓展插件
pip install jupyter_nbextensions_configurator jupyter_contrib_nbextensions
然后进行相应的设置
jupyter contrib nbextension install --user
jupyter nbextensions_configurator enable --user
?设置结束后,重启jupyter
打开后会出现这个图标
点击进去对其进行设置
?2.相关库的准备
pip install opencv-contrib-python -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install mediapape -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install cvzone -i https://pypi.tuna.tsinghua.edu.sn/simple
?3.虚拟计算器的实现
3.1算法流程
1.打开摄像头
2.创建窗口类->绘制计算器的显示窗口
计算器的按键由外边框,内部填充,以及显示的按键文本表示,具有统一的属性,因此我们可以讲这些属性提取出来构造一个按键类
3.手部检测->点击操作->实现计算功能
3.2具体代码以及代码讲解
#创造按键类
class Button:
#初始化传入pos按键位置,每个矩形框的宽高,矩形框的文本数据
def __init__(self, pos, width, height, value):
#初始化在循环前完成
self.pos = pos
self.width = width
self.height = height
self.value = value
#绘图方法在循环后完成
def draw(self, frame):
#绘制计算器轮廓,img画板 起点pos 终点pos 颜色填充
cv2.rectangle(frame, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height), (255,255,255), cv2.FILLED)
#添加边框
cv2.rectangle(frame, self.pos, (self.pos[0]+self.width, self.pos[1]+self.height), (50,50,50), 3)
#添加按键文本
cv2.putText(frame,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+heigth 控制行
if self.pos[0] < x < self.pos[0] + self.width and self.pos[1] < y < self.pos[1] + self.height:
#如果点击改变按钮颜色,并将文本数据绘制到数据框内
cv2.rectangle(frame, self.pos, (self.pos[0]+self.width,self.pos[1]+self.height),(0,255,255),cv2.FILLED)
cv2.rectangle(frame, self.pos, (self.pos[0]+self.width,self.pos[1]+self.height),(50,50,50),3)
cv2.putText(frame, self.value, (self.pos[0]+30, self.pos[1]+70), cv2.FONT_HERSHEY_COMPLEX, 2, (0,0,255), 5)
return True
else:
return False
import cv2
from cvzone.HandTrackingModule import HandDetector
import mediapipe as mp
import time
import cvzone
import pandas as pd
import numpy as np
cap=cv2.VideoCapture(0)
#解决宽高问题 cv2.VideoCapture().get(propId)
#3:CV_CAP_PROP_FRAME_WIDTH 视频流中帧的宽度。
#4:CV_CAP_PROP_FRAME_HEIGHT 视频流中帧的高度
cap.set(3, 1280)#设置框宽1080
cap.set(4, 720)#设置宽高720
pTime = 0#设置第一帧开始处理的起始时间
#手部检测,置信度为0.8 最多检测一只手
detector = HandDetector(detectionCon=0.8, maxHands = 1)
#2.1创建计算器按键
#创建value列表
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坐标 y=150
#传入起点坐标及宽高
button1 = Button((xpos, ypos), 100, 100, buttonListvalues[y][x])
#将确定坐标的矩形框信息存入列表
buttonList.append(button1)
"""
由于首先进行内部循环,绘制时是逐列绘制,绘制完成后buttonList= {'7','4','1','0','8','5','2','/','9','6','3','.','*','-','+','='}
"""
myEquation = ''
delayCounter = 0
while True:
ret, frame = cap.read()
#反转图像
#解决镜像问题
frame = cv2.flip(frame, flipCode = 1)
#检测手部关键点,返回关键点的坐标和绘制后的图像
hands, frame = detector.findHands(frame, flipType=False,draw=True)
#绘制计算器
#首先绘制结果显示部分 width=400
cv2.rectangle(frame, (800,50), (800+400,70+100), (255,255,255), cv2.FILLED)
cv2.rectangle(frame, (800,50),(800+400, 70+100), (50,50,50), 3)
#遍历list 调用draw 绘制每个按键
for button in buttonList:
button.draw(frame)
#检测手指点击按键
if hands:
#cv2.waitKey(10000)
lmlist = hands[0]['lmList']
#print(np.shape(lmlist))
#在实际运行时,发现lmlist传入的参数有误,将lmlist的形状打印,查看其参数,进行调试
#print(np.shape(lmlist[8]))
#这里的lmlist中储存的有三个参数,但在findDistance()函数中需要传递的lmlist中的参数只有两个,分别为p1和p2的(x,y)坐标,这两个参数在lmList的前两个参数,因此对lmlist进行切片操作,得到(x,y)坐标
lmlist[8]=lmlist[8][:2]
lmlist[12]=lmlist[12][:2]
#lmlist=pd.iloc(lmlist,[0,'lmList'])
#获取食指和中指指尖距离并绘线
#返回指尖连线长度,线条信息,绘制后的图像
length, _, frame = detector.findDistance(p1=lmlist[8], p2=lmlist[12], img=frame)
#print(np.shape(_))
#获取食指坐标
x, y=lmlist[8]
#print(length)
#如果指尖距离小于50,找到按下了哪个键
if length < 50:
#遍历所有按键
#print(np.shape(buttonList))
#enumrate() 将传入的列表进行处理,返回成索引一一对应其中的参数
#enumerate return iteration start
for i,button in enumerate(buttonList):
#点击按键 按键颜色发生变化 返回True 延时器为0才能运行
if button.checkClick(x, y) and delayCounter==0:
#计算
#找到点击按钮的编号 i 0-15 转化成索引[i%4][i//4]
myValue=buttonListvalues[i%4][i//4]
if myValue== '=':
myEquation = str(eval(myEquation))
#eval 将字符串数据作为表达式返回一个数值
else:
myEquation+=myValue#字符串直接相加
#time.sleep(0.2)
#为了防止重复点击,使用一个计数器控制每次点击的间隔时长
#计数器 一次点击了一个键
delayCounter = 1
#点击一个按钮以后delayCounter=1 20帧以后才能点击下一个
if delayCounter != 0:
delayCounter += 1 #延迟一帧
if delayCounter > 50: #10帧过去后才能再次点击
delayCounter = 0
#绘制计算表达式
cv2.putText(frame, myEquation, (800+10,100+20), cv2.FONT_HERSHEY_PLAIN, 3, (50,50,50), 3)
#查看FPS
cTime = time.time()#处理完一帧图片的时间
fps = 1/(cTime-pTime)
pTime = cTime #重置起始时间
#在视频上显示fps信息,先转换成整数再变成字符串形式,文本显示坐标,文本字体,文本大小
cv2.putText(frame, str(int(fps)),(70,50),cv2.FONT_HERSHEY_PLAIN,3,(255,0,0))
cv2.imshow("image",frame)
#每帧滞留时间
key = cv2.waitKey(1)
#清空计算框
if key == ord('c'):
myEquation = ''
#退出
if key & 0xFF==27:
break
#if cv2.waitKey(100) & 0xFF == ord('q'):
# break
#释放内存
cap.release()
cv2.destoryAllWindows()
3.3运行结果
?
笔者对于计算机视觉相关知识也只是学习阶段,如有改进想法或疑问可以互相交流,
邮箱:2806762759@qq.com
|