基于摄像头的简单手势控制系统系统,主要利用Pytnon语言和控制摄像头的opencv库,识别和获取手部信息的mediapipa库以及控制鼠标和键盘的pyautogui库。 通过对手指竖起的数量和特定的手势来实现人机交互,从而来实现控制系统的所有功能。 使用这个系统进行的人机交互可以实现电脑的一些常用 *** 作。
大致功能如下:
a) 控制电脑音量大小。
b) 控制鼠标的移动,能大致完成鼠标的所有功能。 比如鼠标的移动,单击左键或右键,拖动文件,画图,滑动等
c) 通过手的左右移动来实现键盘左右方向键的功能。 比如PPT的翻页,文件和网页的浏览等.
安装:下载Python 3.8 和 PyCharm Community Edition 2021.3.3(安装路径尽量不要出现中文)
下载opencv-python,mediapiia,pyautogui,numpy,pycaw,comtypes库
作品思路:
首先,构建了手指、手指形状、手指个数与系统功能间的对应关系,确定交互系统的大致设计框架和方向;然后根据实际需求选择了Python作为本交互系统的设计语言;最后根据系统的需求,选取合适的Python库及其方法:
a) 根据作品的概念设计上使用opencv和mediapipa这两个库的方法并进行初步的测试了解后,开始设计大致的判断方法以及对信息的获取。 比如对于手指的坐标,判断是否竖起,计算两指间的距离等。 再设相应的实现代码测量并改进。
b) 开始查询并设计控制系统的功能。 根据作品大致的框架和概念设计来进行控制系统的功能设计,并在网上查询对应的库和使用方法。 开始设计代码
功能包括鼠标的移动,滑动,单击左右键,拖动,控制音量,键盘的左右方向键。
c) 测试代码的效果。
d) 完善系统的功能,提升执行效果。
这是Hand.py
import cv2
import math
import mediapipe as md
class HandDetector():
def __init__(self):
self.hand_detector = md.solutions.hands.Hands()
self.length = 0
self.label = "Right"
def hand(self, img, draw=True):
img_Rgb = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) #改变视屏色彩
self.hand_date = self.hand_detector.process(img_Rgb)
if draw:
if self.hand_date.multi_hand_landmarks:
for handlms in self.hand_date.multi_hand_landmarks:
md.solutions.drawing_utils.draw_landmarks(img, handlms, md.solutions.hands.HAND_CONNECTIONS)
def hand_position(self, img):
h, w, c = img.shape
self.position = {'Left': {}, 'Right': {}} #定义一个字典
if self.hand_date.multi_hand_landmarks:
i = 0
for point in self.hand_date.multi_handedness:
score = point.classification[0].score
if score > 0.8: #大于百分之八十是哪只手
self.label = point.classification[0].label
hand_lms = self.hand_date.multi_hand_landmarks[i].landmark
for id, lm in enumerate(hand_lms):
x, y = int(lm.x * w), int(lm.y * h)
self.position[self.label][id] = (x, y)
#print(label) #输出是那只手
i =i + 1
return self.position, self.label
def finger_up(self, hand='Left'): #获取竖起的手指数量和哪只手
self.tips = {4, 8, 12, 16, 20}
self.tips_count = {4: 0, 8: 0, 12: 0, 16: 0, 20: 0}
for tip in self.tips:
self.tip1 = self.position[hand].get(tip, None)
self.tip2 = self.position[hand].get(tip-2, None)
if self.tip1 and self.tip2:
if tip == 4:
if self.tip1[0] > self.tip2[0]:
if hand == 'Left':
self.tips_count[tip] = 1
else:
self.tips_count[tip] = 0
else:
if hand == 'Left':
self.tips_count[tip] = 0
else:
self.tips_count[tip] = 1
else:
if self.tip1[1] > self.tip2[1]:
self.tips_count[tip] = 0
else:
self.tips_count[tip] = 1
return list(self.tips_count.values()).count(1), self.tips_count
def handtips_distance(self, img, rp1, rp2, hand = 'Right'): #计算两指间的距离
self.length = 0
#cx, cy = 0, 0
Right_finger1 = self.position[hand].get(rp1, None)
Right_finger2 = self.position[hand].get(rp2, None)
if Right_finger1 and Right_finger2:
cv2.circle(img, (Right_finger1[0], Right_finger1[1]), 10, (255, 0, 0), cv2.FILLED)
cv2.circle(img, (Right_finger2[0], Right_finger2[1]), 10, (255, 0, 0), cv2.FILLED)
cv2.line(img, Right_finger1, Right_finger2, (144, 0, 255))
x1, y1 = Right_finger1[0], Right_finger1[1]
x2, y2 = Right_finger2[0], Right_finger2[1]
#cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
self.length = math.hypot((x2 - x1), (y2 - y1))
return self.length
这是主函数
import cv2
import pyautogui as pb
from HandWay import HandDetector
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
import numpy as nb
import math, time
camera = cv2.VideoCapture(1, cv2.CAP_DSHOW) #使用摄像头,视屏捕捉
camera.set(3, 1080) #设置摄像头屏幕的大小
camera.set(4, 720)
hand_detector = HandDetector()
devices = AudioUtilities.GetSpeakers() #初始化windows音频控制对象
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) #调用系统的音频控制接口
volume = cast(interface, POINTER(IAudioEndpointVolume))
volRange = volume.GetVolumeRange() #获取电脑音量范围
frameR = 50
plocx, plocy = 0, 0
finger_count = {'Left': {}, 'Right': {}}
tip = {'Left': {4: 0, 8: 0, 12: 0, 16: 0, 20: 0}, 'Right': {4: 0, 8: 0, 12: 0, 16: 0, 20: 0}}
#获取电脑最大最小音量
minVol = volRange[0]
maxVol = volRange[1]
while True:
success, img = camera.read() #获取是否读成功和视频帧
if success:
h, w, c = img.shape #获取摄像头的屏幕大小
h1, w1 = pb.size() #获取电脑屏幕的大小
#print(h1, w1)
x, y = pb.position()
#print(x, y)
img = cv2.flip(img, 1)
hand_detector.hand(img, draw=False)
position, label = hand_detector.hand_position(img) #获取手部的信息
center = hand_detector.position[label].get(0, None)
finger_count[label], tip[label] = hand_detector.finger_up(label) #获取竖起的手指数量和哪只手指是否竖起
left_finger_count, tip['Left'] = hand_detector.finger_up('Left')
# print(left_finger_count)
cv2.putText(img, str(left_finger_count), (100, 150), cv2.FONT_HERSHEY_DUPLEX, 5, (0, 0, 255), 3)
right_finger_count, tip['Right'] = hand_detector.finger_up('Right')
# print(right_finger_count)
cv2.putText(img, str(right_finger_count), (w - 200, 150), cv2.FONT_HERSHEY_DUPLEX, 5, (255, 0, 255), 3)
hand_length1 = hand_detector.handtips_distance(img, 8, 12, label)
# print(hand_length1)
hand_length2 = hand_detector.handtips_distance(img, 4, 8, label)
if tip[label][4] == 1 and tip[label][8] == 1 and finger_count[label] == 2: #当大拇指和食指竖起时,改变两指间的距离来调整音量大小
vol = nb.interp(hand_length2, [40, 250], [minVol, maxVol])
print(tip[label][4])
print(vol, hand_length2)
volume.SetMasterVolumeLevel(vol, None)
if finger_count[label] == 5: #当五指全部竖起时,掌心的横坐标向左或右超过某一值时,将执行向左或右的键盘功能
# time.sleep(2)
if label == 'Right':
if center[0] > w - 300:
time.sleep(0.5)
pb.press('right')
print("Right")
else:
if center[0] < w - 460:
time.sleep(0.5)
pb.press('left')
print("Left")
else:
print("center")
else:
if label == 'Left':
if center[0] < 250:
pb.press('left')
print("Left")
else:
if center[0] > 360:
pb.press('right')
print("Right")
else:
print("center")
Right_roll = hand_detector.position[label].get(8, None) #得到食指的相关信息
if Right_roll:
rx, ry = Right_roll[0], Right_roll[1]
if tip[label][8] == 0 and finger_count[label] == 0: #只有食指竖起时,执行鼠标向上滑动的功能,反之向下
pb.scroll(-50, x, y)
print("down")
else:
if tip[label][8] == 1 and finger_count[label] == 1:
pb.scroll(50, x, y)
print("up")
Right_finger1 = hand_detector.position[label].get(8, None)
Right_finger2 = hand_detector.position[label].get(12, None)
if Right_finger1 and Right_finger2:
cx = (Right_finger1[0] + Right_finger2[0]) // 2 #得到食指和中指的中心坐标
cy = (Right_finger1[1] + Right_finger2[1]) // 2
tx = nb.interp(cx, (frameR, w - frameR), (0, w1)) #转化为屏幕坐标
ty = nb.interp(cy, (frameR, h - frameR), (0, h1))
if tip[label][8] == 1 and tip[label][12] == 1 and finger_count[label] == 2: #当左手或右手的食指和中指竖起时,通过两指的指尖中心的坐标来控制移动鼠标
pb.FAILSAFE = False
tip_x = (plocx + (tx - plocx)) * 2
tip_y = plocy + (ty - plocy)
time.sleep(0.01)
pb.moveTo(tip_x, tip_y)
plocx, plocy = tip_x, tip_y #记录上次的位置
if hand_length1 < 52: #当两指的指尖距离小于52mm时一直执行鼠标的左键单击功能
pb.mouseDown(button='left')#pb.dragTo(tip_x, tip_y, button='left')
print("first")
cv2.circle(img, (cx, cy), 15, (144, 144, 144), cv2.FILLED)
else:
pb.mouseUp(button='left')
#当大拇指,食指以及中指竖起时,执行鼠标的左键点击:
if tip[label][4] == 1 and tip[label][8] == 1 and tip[label][12] == 1 and finger_count[label] == 3:
pb.leftClick()
print("Left")
#当食指和中指以及无名指和小拇指竖起时,执行鼠标的右键点击
if tip[label][8] == 1 and tip[label][12] == 1 and tip[label][16] == 1 and tip[label][20] == 1 and finger_count[label] == 4:
pb.rightClick()
print("Right")
cv2.imshow('Hand', img) #视频窗口
if cv2.waitKey(1) & 0XFF == 27: #按下Esc按键退出
break
camera.release()
cv2.destroyAllWindows()
运行说明:打开摄像头(如果使用电脑默认的,在源代码中的camera = cv2.VideoCapture(1, cv2.CAP_DSHOW的1改为0)
使用方法:
根据作品的安装说明来进行安装相应的编译器,打开摄像头,进行功能执行时将手心对准摄像头。
详细的功能执行 *** 作如下:
- 利用改变大拇指和食指间的距离来改变电脑音量的大小。
- 将食指和中指竖起时,两指间的中心坐标会作为鼠标的坐标来控制鼠标的移动。
并且两指贴合时,执行鼠标一直单击左键的功能。
- 当大拇指,食指和中指同时竖起时,执行鼠标的单击左键的功能。
- 仅大拇指未竖起时,执行鼠标的单击右键的功能。
- 当只有食指竖起时,执行鼠标的滑轮向上的功能,反之,执行鼠标的滑轮向下的功能。
- 当五指全都竖起时,向左(右)移动一定距离时,每隔0.5秒执行的键盘的左(右)方向键的功能。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)