众所周知,华为早在P40手机上实现了隔空手势截屏的功能,使用者只需要在手机前对着屏幕“握拳”或“抓握”,即可对手机进行截屏。而随去年11月底Mate 70的发布,一项“震惊中外”新功能被正式推送——“手势隔空传输文件”。
如果看了华为对“手势隔空传输文件”的宣传片后相信你一定会被华为无线短距离通信和人工智能方面的进步感到震惊,我看完宣传片后也是觉得不可思议。
最近在寻找Snipaste的“替代品”的时候突然想起来有这么一回事,然后就去搜了搜有没有适用于Windows平台的类似于“隔空手势截图”的这么一个软件,结果呢,肯定是什么都没有搜到,要不然我也不会自己搓一个出来
问了一下DeepSeek,华为“手势隔空传输文件”这个功能主要是由以下几个步骤来实现的:
-
核心在于人工智能算法与传感器技术的完美结合,通过高精度传感器(如姿态感应器、摄像头等),设备能够精准捕捉用户的手势动作,并将这些动作转化为数字信号供AI算法分析。
-
采用了先进的短距离无线通信技术,确保了手机与其他设备之间能够迅速建立稳定的连接并传输数据。(深度查找了一下相关资料,我猜测这个功能可能使用了WiFi-Direct)
-
鸿蒙系统(HarmonyOS NEXT)为AI隔空传送提供了底层支持,确保了功能的稳定性和流畅性。
因为设备原因,没有财力采购先进的AI摄像头传感器(就算是低端的也要好几百...),因此就使用了普通的摄像头配合算法,所以,我搓出来的这个应用在你的Windows电脑上也是可以使用的(当然前提是你得有一台搭载前置摄像头的电脑,并且内存和CPU都还说得过去)
以下是我开发时的环境:
Windows 11 24H2
Python 3.9.13
第三方库:(Requirements)
opencv-python v4.7.0.68
mediapipe v0.10.21
numpy v1.26.4
pyautogui v0.9.54
请在你的项目目录下安装上面的这些第三方库,如果出现安装问题,可跳转至我两年前在本站发过的一篇处理文章,链接在这👉Python安装第三方库时的报错处理https://blog.youkuaiyun.com/Mike0010/article/details/128738664这一期发布刚搓出来的v1.0.a版本,主要实现也唯一实现的一个功能是:隔空手势截图。
先来看一下运行过程:
“仿”华为隔空手势截屏0.1.a演示
目录
3、定义握拳状态:check_fist(hand_landmarks)
1、导入库与配置
在安装完成第三方库之后,需要先导入库以及初始化。
import cv2
import mediapipe as mp
import numpy as np
import time
import os
import pyautogui
# 初始化mediapi的手pe部检测模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 创建截图存储目录
if not os.path.exists("screenshots"):
os.makedirs("screenshots")
# 初始化摄像头
cap = cv2.VideoCapture(0)
# 检查摄像头是否打开成功
if not cap.isOpened():
print("无法打开摄像头")
exit()
# 初始化是否已截图的标志
screenshot_taken = False
screenshot_counter = 0
cooldown_time = 3 # 冷却时间(秒)
last_screenshot_time = 0
previous_hand_state = False
# 初始化已截图信息显示的标志和持续时间
show_message = False
message_duration = 2 # 信息显示的持续时间(秒)
message_start_time = 0
OpenCV和NumPy应该对你来说并不陌生,或多或少都应该略有耳闻,前者主要用于视觉静动态分析,后者主要用于数据分析。目前Python生态对于这两个库的运用都已经非常成熟,这边再介绍一下这次用到的另外两个库——Mediapipe和PyAutoGUI。
Mediapipe也是一个视觉分析库,在OpenCV的基础上具备手势分析这一功能,而此项目也正是运用到了这一特性(此外还可用于音频分析、姿态估计等)。PyAutoGUI则是一个自动化库,用于本项目所涉及的截图功能。
以上的第一部分的代码主要定义了一些变量和截图的保存路径,我用DeepSeek为一些代码加上了注释以方便你阅读和理解。
2、定义截图函数:take_screenshot()
def take_screenshot():
"""使用pyautogui捕获屏幕并保存截图"""
global screenshot_counter
filename = f"screenshots/screenshot_{screenshot_counter}.png"
pyautogui.screenshot(filename)
screenshot_counter += 1
print(f"已保存截图: {filename}")
return filename
这一部分代码实现的功能是使用 PyAutoGUI 截图并保存到对应目录,其中 filename 通过一个 f-字符串 来定义(注:f-字符串 仅支持Python 3.6 以上版本!),并在截图完成后打印一条完成信息。
3、定义握拳状态:check_fist(hand_landmarks)
def check_fist(hand_landmarks):
"""检查是否为握拳手势"""
# get all landmark coordinates
landmarks = hand_landmarks.landmark
# 检查手指是否弯曲
# 区分握拳与松开
# 拳头手势:对于每个手指,尖端到掌心的距离小于手的大小的一定比例
# 获取手的整体位置(基于手腕)
wrist_x = landmarks[mp_hands.HandLandmark.WRIST].x
wrist_y = landmarks[mp_hands.HandLandmark.WRIST].y
# 计算手的大小(从手腕到食指关节的距离)
hand_size = np.sqrt((landmarks[mp_hands.HandLandmark.INDEX_FINGER_MCP].x - wrist_x) ** 2 +
(landmarks[mp_hands.HandLandmark.INDEX_FINGER_MCP].y - wrist_y) ** 2) * 100
# 检查每个手指指尖的是否接近掌心
thumb_tip = landmarks[mp_hands.HandLandmark.THUMB_TIP]
index_tip = landmarks[mp_hands.HandLandmark.INDEX_FINGER_TIP]
middle_tip = landmarks[mp_hands.HandLandmark.MIDDLE_FINGER_TIP]
ring_tip = landmarks[mp_hands.HandLandmark.RING_FINGER_TIP]
pinky_tip = landmarks[mp_hands.HandLandmark.PINKY_TIP]
palm_center = landmarks[mp_hands.HandLandmark.MIDDLE_FINGER_MCP]
# 检查每个手指是否弯曲
thumb_bent = np.sqrt((thumb_tip.x - palm_center.x) ** 2 + (thumb_tip.y - palm_center.y) ** 2) * 100 < hand_size * 0.6
index_bent = np.sqrt((index_tip.x - palm_center.x) ** 2 + (index_tip.y - palm_center.y) ** 2) * 100 < hand_size * 0.6
middle_bent = np.sqrt((middle_tip.x - palm_center.x) ** 2 + (middle_tip.y - palm_center.y) ** 2) * 100 < hand_size * 0.6
ring_bent = np.sqrt((ring_tip.x - palm_center.x) ** 2 + (ring_tip.y - palm_center.y) ** 2) * 100 < hand_size * 0.6
pinky_bent = np.sqrt((pinky_tip.x - palm_center.x) ** 2 + (pinky_tip.y - palm_center.y) ** 2) * 100 < hand_size * 0.6
# 如果所有手指都弯曲,则判定为握拳
return thumb_bent and index_bent and middle_bent and ring_bent and pinky_bent
这一部分代码是整个程序的核心,用于检测握拳状态,原理是检测每个手指的指尖到掌心的距离是否小于整个手的长度,当五个手指的判断结果都为弯曲时,就返回True。
4、主程序
print("手势截图功能已启动!当您在摄像头前握时拳,将自动截图。")
while cap.isOpened():
ret, frame = cap.read()
if not ret:
continue
# 将BGR图像转换为RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
current_time = time.time()
# 处理图像并检测手部
results = hands.process(rgb_frame)
# 如果检测到手部
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
current_hand_state = check_fist(hand_landmarks)
mp_drawing.draw_landmarks(
frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# 检查是否为握拳手势
if current_hand_state:
if not previous_hand_state and current_time - last_screenshot_time >= cooldown_time:
# 截图
filename = take_screenshot()
# 更新最后截图时间
last_screenshot_time = current_time
show_message = True
message_start_time = current_time
# 显示截图完成的消息
#cv2.putText(frame, "Captured!", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
current_time = time.time()
previous_hand_state = current_hand_state
if show_message:
if current_time - message_start_time < message_duration:
# 显示截图完成的消息
cv2.putText(frame, f"Captured! ({screenshot_counter - 1})", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1,
(0, 255, 0), 2)
else:
# 重置标志
show_message = False
# 重置标志,以便进行下一次截图
# time.sleep(1) # 给用户一点时间看截图消息
else:
# 重置标志,以便在下一个握拳动作时截图
screenshot_taken = False
# 可视化手部 landmarks(可选)
# 显示视频流
cv2.imshow('ges_capture', frame)
# 扌 'q' 退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
print("程序已结束。")
当摄像头打开时,就运行主程序。你可以配合着注释理解一下代码,如果有不理解的,欢迎在文末评论。
5、完整程序
(谁让你拉滚动条了???就知道抄代码...)
完整程序就是把上面的所有代码按顺序写到一个PY文件里就行了,这里就不再贴出来了。
结语
以上就是本期的全部内容了,后续我会想想怎么实现隔空传输,或者继续尝试华为的隔空手势滑屏等功能,欢迎继续关注本站!
创作不易,如果你喜欢本文,就请留下你的“点赞”和“收藏”吧,我们下次再见!