基于摄像头识别的手势控制系统,主要利用Python语言中的Opencv,mediapipa和 pyautogui这两个库,来打开并控制电脑摄像头,使用mediapipa库中的solution方法来对手部信息进行识别和获取。通过对手指竖起的个数以及特定的手指来实现并执行手势控制系统的所有功能。
大致功能如下:
a) 控制电脑音量大小。
b) 控制鼠标的移动,能大致完成鼠标的所有功能。比如鼠标的移动,单击左键或右键,拖动文 件,画图,滑动等
c) 通过手的左右移动来实现键盘左右方向键的功能。比如PPT的翻页,文件和网页的浏览等。
作品的安装与运行说明
安装:下载Python 3.8 和 PyCharm Community Edition 2021.3.3
下载opencv-python,mediapiia,pyautogui,numpy,pycaw,comtypes库
运行说明:打开摄像头(如果使用电脑默认的,在源代码中的camera = cv2.VideoCapture(1, cv2.CAP_DSHOW的1改为0)
使用方法:
根据作品的安装说明来进行安装相应的编译器,打开摄像头,进行功能执行时将手心对准摄像头。
详细的功能执行操作如下:
- 利用改变大拇指和食指间的距离来改变电脑音量的大小。
- 将食指和中指竖起时,两指间的中心坐标会作为鼠标的坐标来控制鼠标的移动。并且两指贴合时,执行鼠标一直单击左键的功能。
- 当大拇指,食指和中指同时竖起时,执行鼠标的单击左键的功能。
- 仅大拇指未竖起时,执行鼠标的单击右键的功能。
- 当只有食指竖起时,执行鼠标的滑轮向上的功能,反之,执行鼠标的滑轮向下的功能。
- 当五指全都竖起时,向左(右)移动一定距离时,每隔0.5秒执行的键盘的左(右)方向键的功能。
这里是手部方法的代码
import cv2
import math
import mediapipe as mdclass HandDetector():def __init__(self):self.hand_detector = md.solutions.hands.Hands()self.length = 0self.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.shapeself.position = {'Left': {}, 'Right': {}} #定义一个字典if self.hand_date.multi_hand_landmarks:i = 0for point in self.hand_date.multi_handedness:score = point.classification[0].scoreif score > 0.8: #大于百分之八十是哪只手self.label = point.classification[0].labelhand_lms = self.hand_date.multi_hand_landmarks[i].landmarkfor 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 + 1return self.position, self.labeldef 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] = 1else:self.tips_count[tip] = 0else:if hand == 'Left':self.tips_count[tip] = 0else:self.tips_count[tip] = 1else:if self.tip1[1] > self.tip2[1]:self.tips_count[tip] = 0else:self.tips_count[tip] = 1return list(self.tips_count.values()).count(1), self.tips_countdef handtips_distance(self, img, rp1, rp2, hand = 'Right'): #测量两指间的距离self.length = 0#cx, cy = 0, 0Right_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) // 2self.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, timecamera = 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: #当五指全部竖起时,掌心的横坐标向左或右超过某一值时,将执行向左或右的键盘功能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)"""hand_length3 = hand_detector.handtips_distance(img, 9, 12)hand_length4 = hand_detector.handtips_distance(img, 0, 9)hand_length5 = hand_detector.handtips_distance(img, 0, 12)hand_length6 = hand_detector.handtips_distance(img, 5, 8)hand_length7 = hand_detector.handtips_distance(img, 0, 5)hand_length8 = hand_detector.handtips_distance(img, 0, 8)"""if Right_finger1 and Right_finger2:cx = (Right_finger1[0] + Right_finger2[0]) // 2 #得到食指和中指的中心坐标cy = (Right_finger1[1] + Right_finger2[1]) // 2tx = nb.interp(cx, (frameR, w - frameR), (0, w1)) #转化为屏幕坐标ty = nb.interp(cy, (frameR, h - frameR), (0, h1))#print(cx, cy)输出中点坐标if tip[label][8] == 1 and tip[label][12] == 1 and finger_count[label] == 2: #当左手或右手的食指和中指竖起时,通过两指的指尖中心的坐标来控制移动鼠标pb.FAILSAFE = Falsetip_x = (plocx + (tx - plocx)) * 2tip_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')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按键退出breakcamera.release()
cv2.destroyAllWindows()
作品思路
首先,确认作品的大致的框架和方向,选择需要的编译语言和相应的编译软件。再确认完成后,选择了Python语言。在网上搜索并确认所需的Python库和使用方法。
- 根据作品的概念设计上使用opencv和mediapipa这两个库的方法并进行初步的测试了解后,开始设计大致的判断方法以及对信息的获取。然后设计相应的实现代码测量并改进。
- 开始查询并设计控制系统的功能。根据作品大致的框架和概念设计来进行控制系统的功能设计,并在网上查询对应的库和使用方法。开始设计代码
- 测试代码的效果。
- 完善系统的功能,提升执行效果。
作品应用场景:
可以适用于多媒体教学的智能教育方面,在课上可以利用手的移动来去移动PPT来更方便教学,以及画图和划重点时可以不用往返移动。
日常的观看电视剧或者其他视频不方便用手时,就可以利用本操作系统来去进行快进,改变音量等操作。
设计理念:
利用手部的一些简单的动作来执行在生活中常用的一些基本操作,能更加方便来解决和提高生活中的问题和趣味性。利用手指的竖起和放下的简单操作和摄像头的捕捉和识别来隔空操作电脑
技术方案:
主要利用Python语言中的Opencv库来打开并控制电脑摄像头,pyautogui库来控制鼠标的移动和简单按键,mediapipa库来识别手部的信息和使用mediapipa库中的solution方法来对手部信息进行识别和获取。使用pycaw库来获取Windows电脑音频控制,并使用comtypes库来调用系统的音频控制接口。Ctypes库提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。