Python-基于mediapipe,pyautogui,cv2和numpy的电脑手势截屏工具(进阶版)
前言:在我们的日常生活中,手机已经成为我们每天工作,学习,生活的一个不可或缺的部分。众所周知:为了我们的使用方便,手机里面的很多功能非常人性化,既便捷又高效,其中就有手机的截屏方式,它们花样繁多,如三指截屏,手势截屏等。那么怎么在电脑里面也实现这个功能呢?(虽然我们知到电脑也有快捷的截屏方式-Ctrl+Shift+S。但是很明显,这依然不够快捷,因为这至少需要用户的两次手动操作)。那么废话不多说,我们直接开始今天的Python学习之路-利用PyCharm手搓一个基于mediapipe,pyautogui,cv2和numpy的电脑手势截屏工具。
编程思路:本次编程我们需要pyautogui来获取屏幕的尺寸,这对于确定按钮的位置和大小非常有用;接着利用mediapipe初始化一个手部模型,用来检测视频流中的手部关键点;numpy提供了计算机在采集了用户手部姿态所得到的数据的处理,并与mediapipe所建立的数学模型进行比较等。cv2是本次编程的重头戏,它为调用计算机摄像头进行信息采集,以及用户手部模型的实时可视化展现等提供了可能。这次我们额外添加了win32com库,用于语音提醒用户程序初始化进程。此外,我们将截屏所得图片的存放位置改为系统的图片文件夹中,更接近普通的截屏操作(这个需要调用os库操作系统,pathlib库获取文件位置,time库获取时间戳等)。
第一步:导入库
本次编程所需调用的库:
1,标准库:cv2,numpy,time,os,pathlib。
2,第三方库:mediapipe,pyautogui,win32com。
# 导入必要库
import cv2
import mediapipe as mp
import pyautogui
import numpy as np
import os
from pathlib import Path
import time
import win32com.client
第二步:程序初始化
我们需要初始化判断模型并给出屏幕尺存等相关变量参数。此外,我们同时也需要初始化语音播放器音量,语速及图片的保存路径等。
# 初始化语音播报器
speaker = win32com.client.Dispatch("SAPI.SpVoice")
speaker.Rate = 1 # 设置语速
speaker.Volume = 100 # 设置音量
speaker.Speak("欢迎使用手势截图工具")
# 初始化保存路径
save_folder = get_pictures_path() / "Gesture_Screenshots"
save_folder.mkdir(parents=True, exist_ok=True)
# 初始化MediaPipe手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.7)
mp_draw = mp.solutions.drawing_utils
# 屏幕尺寸和状态变量
screen_w, screen_h = pyautogui.size()
waiting_for_click = False
blink_counter = 0
screenshot_taken = False
btn_rect = (500, 400, 200, 60)
# 自定义紫色调色板
PURPLE_POINT = (255, 0, 255) # BGR格式-亮紫色
PURPLE_LINE = (200, 0, 200) # BGR格式-深紫色
第三步:构建内部逻辑
接下来我们需要编写程序内部判断模型等的内在逻辑,并将鼠标,按键事件与计算机采集信息所得的对比结果联系起来。此外,我们还会构建图片的路径函数以将图片存放至指定位置。
# 获取图片保存路径函数
def get_pictures_path():
if os.name == 'nt':
return Path.home() / "Pictures"
else:
xdg_pics = os.environ.get('XDG_PICTURES_DIR')
return Path(xdg_pics) if xdg_pics else Path.home() / "Pictures"
def mouse_click(event, x, y, flags, param):
global waiting_for_click, screenshot_taken
if event == cv2.EVENT_LBUTTONDOWN:
if (btn_rect[0] < x < btn_rect[0] + btn_rect[2] and
btn_rect[1] < y < btn_rect[1] + btn_rect[3]):
waiting_for_click = False
screenshot_taken = False
def draw_button(frame):
x, y, w, h = btn_rect
cv2.rectangle(frame, (x, y), (x + w, y + h), (50, 200, 50), -1)
cv2.rectangle(frame, (x, y), (x + w, y + h), (30, 180, 30), 2)
cv2.putText(frame, "CONTINUE", (x +10, y + 40),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
def is_thumbs_up(hand_landmarks):
tips = [8, 12, 16, 20]
thumb_tip = hand_landmarks.landmark[4]
thumb_pip = hand_landmarks.landmark[3]
thumb_vertical = abs(thumb_tip.y - thumb_pip.y) < 0.05
fingers_folded = True
wrist = hand_landmarks.landmark[0]
for tip_index in tips:
tip = hand_landmarks.landmark[tip_index]
if abs(tip.y - wrist.y) > 0.1:
fingers_folded = False
return thumb_vertical and fingers_folded
第四步:搭建屏幕及内容显示设置
这里我们需要对截屏的各个步骤进行拆分,并对每个步骤的执行结果进行相应的处理(如屏闪,实时显示,操作完成提醒等)。
"""屏幕显示区"""
cap = cv2.VideoCapture(0)
cv2.namedWindow('Gesture Screenshot')
cv2.setMouseCallback('Gesture Screenshot', mouse_click)
while cap.isOpened():
success, frame = cap.read()
if not success:
continue
frame = cv2.flip(frame, 1)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 手部检测与绘制
results = hands.process(rgb_frame)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 紫色骨骼连线(修改部分)
mp_draw.draw_landmarks(
frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_draw.DrawingSpec(color=PURPLE_POINT, thickness=3, circle_radius=4),
mp_draw.DrawingSpec(color=PURPLE_LINE, thickness=4)
)
if not waiting_for_click and is_thumbs_up(hand_landmarks):
blink_counter = 10
waiting_for_click = True
# 全屏截图逻辑(修改部分)
if blink_counter > 0:
overlay = frame.copy()
cv2.rectangle(overlay, (0, 0), (screen_w, screen_h), (255, 255, 255), -1)
cv2.addWeighted(overlay, 0.3, frame, 0.7, 0, frame)
blink_counter -= 1
if blink_counter == 0 and not screenshot_taken:
# 使用pyautogui截取全屏
screenshot = pyautogui.screenshot()
timestamp = int(time.time() * 1000)
save_path = save_folder / f"screen_{timestamp}.png"
# 转换PIL图像为OpenCV格式并保存
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
cv2.imwrite(str(save_path), screenshot)
screenshot_taken = True
# 界面提示信息
if waiting_for_click:
if screenshot_taken:
cv2.putText(frame, "SCREENSHOT SAVED!", (btn_rect[0] -480, btn_rect[1] - 360),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
cv2.putText(frame, "Please click 'CONTINUE' to next screenshot!", (btn_rect[0] - 480, btn_rect[1] - 320),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
draw_button(frame)
else:
cv2.putText(frame, "Show THUMBS UP to Capture", (25, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
cv2.putText(frame, "Current Mode: FULL SCREEN", (25, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
cv2.imshow('Gesture Screenshot', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
第五步:释放计算机内部资源
# 释放资源
cap.release()
cv2.destroyAllWindows()
第六步:完整代码展示
# 导入必要库
import cv2
import mediapipe as mp
import pyautogui
import numpy as np
import os
from pathlib import Path
import time
import win32com.client
# 初始化语音播报器
speaker = win32com.client.Dispatch("SAPI.SpVoice")
speaker.Rate = 1 # 设置语速
speaker.Volume = 100 # 设置音量
speaker.Speak("欢迎使用手势截图工具")
# 获取图片保存路径函数
def get_pictures_path():
if os.name == 'nt':
return Path.home() / "Pictures"
else:
xdg_pics = os.environ.get('XDG_PICTURES_DIR')
return Path(xdg_pics) if xdg_pics else Path.home() / "Pictures"
# 初始化保存路径
save_folder = get_pictures_path() / "Gesture_Screenshots"
save_folder.mkdir(parents=True, exist_ok=True)
# 初始化MediaPipe手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.7)
mp_draw = mp.solutions.drawing_utils
# 屏幕尺寸和状态变量
screen_w, screen_h = pyautogui.size()
waiting_for_click = False
blink_counter = 0
screenshot_taken = False
btn_rect = (500, 400, 200, 60)
# 自定义紫色调色板
PURPLE_POINT = (255, 0, 255) # BGR格式-亮紫色
PURPLE_LINE = (200, 0, 200) # BGR格式-深紫色
def mouse_click(event, x, y, flags, param):
global waiting_for_click, screenshot_taken
if event == cv2.EVENT_LBUTTONDOWN:
if (btn_rect[0] < x < btn_rect[0] + btn_rect[2] and
btn_rect[1] < y < btn_rect[1] + btn_rect[3]):
waiting_for_click = False
screenshot_taken = False
def draw_button(frame):
x, y, w, h = btn_rect
cv2.rectangle(frame, (x, y), (x + w, y + h), (50, 200, 50), -1)
cv2.rectangle(frame, (x, y), (x + w, y + h), (30, 180, 30), 2)
cv2.putText(frame, "CONTINUE", (x +10, y + 40),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
def is_thumbs_up(hand_landmarks):
tips = [8, 12, 16, 20]
thumb_tip = hand_landmarks.landmark[4]
thumb_pip = hand_landmarks.landmark[3]
thumb_vertical = abs(thumb_tip.y - thumb_pip.y) < 0.05
fingers_folded = True
wrist = hand_landmarks.landmark[0]
for tip_index in tips:
tip = hand_landmarks.landmark[tip_index]
if abs(tip.y - wrist.y) > 0.1:
fingers_folded = False
return thumb_vertical and fingers_folded
"""屏幕显示区"""
cap = cv2.VideoCapture(0)
cv2.namedWindow('Gesture Screenshot')
cv2.setMouseCallback('Gesture Screenshot', mouse_click)
while cap.isOpened():
success, frame = cap.read()
if not success:
continue
frame = cv2.flip(frame, 1)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 手部检测与绘制
results = hands.process(rgb_frame)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 紫色骨骼连线(修改部分)
mp_draw.draw_landmarks(
frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_draw.DrawingSpec(color=PURPLE_POINT, thickness=3, circle_radius=4),
mp_draw.DrawingSpec(color=PURPLE_LINE, thickness=4)
)
if not waiting_for_click and is_thumbs_up(hand_landmarks):
blink_counter = 10
waiting_for_click = True
# 全屏截图逻辑(修改部分)
if blink_counter > 0:
overlay = frame.copy()
cv2.rectangle(overlay, (0, 0), (screen_w, screen_h), (255, 255, 255), -1)
cv2.addWeighted(overlay, 0.3, frame, 0.7, 0, frame)
blink_counter -= 1
if blink_counter == 0 and not screenshot_taken:
# 使用pyautogui截取全屏
screenshot = pyautogui.screenshot()
timestamp = int(time.time() * 1000)
save_path = save_folder / f"screen_{timestamp}.png"
# 转换PIL图像为OpenCV格式并保存
screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
cv2.imwrite(str(save_path), screenshot)
screenshot_taken = True
# 界面提示信息
if waiting_for_click:
if screenshot_taken:
cv2.putText(frame, "SCREENSHOT SAVED!", (btn_rect[0] -480, btn_rect[1] - 360),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
cv2.putText(frame, "Please click 'CONTINUE' to next screenshot!", (btn_rect[0] - 480, btn_rect[1] - 320),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
draw_button(frame)
else:
cv2.putText(frame, "Show THUMBS UP to Capture", (25, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
cv2.putText(frame, "Current Mode: FULL SCREEN", (25, 70),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
cv2.imshow('Gesture Screenshot', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
第七步:操作指南
运行程序后等待一段模型预建立时间,计算机会进行语音提醒:欢迎使用手势截图工具。接着屏幕上会弹出一个新窗口,窗口中会显示用户手部连接点(关节)和连接线(骨骼),屏幕中有两行提示信息:1,"Show THUMBS UP to Capture"(请将点赞手势对准摄像头) 2,"Current Mode:FULL SCREEN"(当前状态:全屏)。用户可将手调整为点赞手势(即大拇指伸直,其余四根手指弯曲在一起),接着调整手与计算机中显示屏的距离(点赞手势对,距离合适就行,与大拇指的具体朝向无关),当计算机摄像头闪烁一下,显示屏中出现以下三条信息:1,"SCREENSHOT SAVED!"(截屏已保存) ;2,"Please click 'CONTINUE' to enable next capture"(请点击"CONTINUE"继续截屏) 3,"CONTINUE"按钮 时表明截屏已完成,你可以选择继续等待或点击"CONTINUE"按钮继续进行截屏操作。(请忽略红色警示)
第八步:运行效果展示


(我是闪云-微星,欢迎你的点赞/关注)
更多推荐



所有评论(0)