在本教程中,我们将使用MediaPipePython 库来检测我们的手势并使用它来控制 Raspberry Pi 媒体播放器。在这里,我们将使用总共六个手势,即开合拳和手的上、下、左、右移动。打开和关闭拳头手势用于播放和暂停视频。上下手势用于增大和减小音量,左右手势用于快进和倒退视频。
构建手势控制媒体播放器所需的组件
树莓派 4
Pi 相机模块
在这里,我们只需要安装了 OpenCV 和 MediaPipe 的 Raspberry Pi 4 和 Pi 摄像头模块。 OpenCV 用于 数字图像处理,而 MediaPipe 用于手部跟踪。数字图像处理最常见的应用是 物体检测、 人脸识别和 人数统计。
什么是媒体管道?
MediaPipe是一个框架,用于构建跨平台(即 Android、iOS、Web、边缘设备)多模式(例如视频、音频、任何时间序列数据)应用机器学习管道,包括快速 ML 推理、经典计算机视觉和媒体处理(例如视频解码)。MediaPipe 发布了各种预构建的 python 和其他语言包,例如:
物体检测
人脸检测
手部追踪
姿势估计
多手追踪
头发分割
MediaPipe Python 包在适用于 Linux、macOS 和 Windows 的 PyPI 上可用。使用以下命令在 Raspberry Pi 4 上安装 MediaPipe:
须藤 pip3 安装 mediapipe-rpi4
如果您没有 Pi 4,则可以使用以下命令将其安装在 Raspberry Pi 3 上:
须藤 pip3 安装 mediapipe-rpi3
在树莓派上安装 OpenCV
在安装 OpenCV 和其他依赖项之前,Raspberry Pi 需要完全更新。使用以下命令将 Raspberry Pi 更新到其最新版本:
sudo apt-get 更新
然后使用以下命令安装在 Raspberry Pi 上安装 OpenCV 所需的依赖项。
sudo apt-get install libhdf5-dev -y
sudo apt-get install libhdf5-serial-dev –y
sudo apt-get install libatlas-base-dev –y
sudo apt-get install libjasper-dev -y
sudo apt-get install libqtgui4 –y
sudo apt-get install libqt4-test –y
之后,使用以下命令在您的 Raspberry Pi 上安装 OpenCV。
pip3 安装 opencv-contrib-python==4.1.0.25
在树莓派上安装PyAutoGUI
PyAutoGUI 是一个跨平台的 GUI 自动化 Python 模块,可让您的 Python 脚本控制鼠标和键盘以自动与其他应用程序交互。PyAutoGUI 适用于 Windows、macOS 和 Linux,并在 Python 2 和 3 上运行。要在 Raspberry Pi 上安装 PyAutoGUI,请运行:
pip3 安装 pyautogui
为媒体控制器编程树莓派
文档末尾给出了使用手势控制媒体播放器的完整代码。在这里,我们将解释代码的重要部分,以便更好地解释。
通过导入 OpenCV、MediaPipe 和 PyAutoGUI 包来启动代码。如前所述,MediaPipe 是手部跟踪的核心包,而 OpenCV 用于图像处理。PyAutoGUI 用于根据手势控制键盘。
导入简历2
将媒体管道导入为 mp
导入pyautogui
在接下来的几行中,我们创建了两个新变量。第一个是mp_drawing,它将用于从 MediaPipe python 包中获取所有绘图实用程序,第二个是mp_hands,用于导入手部跟踪模型。
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
之后定义一个名为findPosition() 的函数。顾名思义,它用于查找食指、中指、无名指和小指的 X、Y 坐标。所有指尖的坐标都将存储在一个名为lmList[]的变量中。
def 手指位置(图像,handNo=0):
lmList = []
如果结果.multi_hand_landmarks:
myHand = results.multi_hand_landmarks[handNo]
对于 id, lm in enumerate(myHand.landmark):
# 打印(id,lm)
h, w, c = image.shape
cx, cy = int(lm.x * w), int(lm.y * h)
lmList.append([id, cx, cy])
返回 lmList
然后从 Raspberry Pi 相机开始视频流,帧高和宽分别为 720、640。
上限 = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
然后在下一行中为 mediapipe 提要设置一个新实例,以访问我们之前导入的手部追踪模型。我们还传递了两个关键字参数,即最小检测置信度和最小跟踪置信度。接下来,我们将读取视频帧并将它们存储在图像变量中。
用 mp_hands.Hands(min_detection_confidence=0.8, min_tracking_confidence=0.5) 作为手:
而 cap.isOpened():
成功,图像 = cap.read()
我们从视频源获得的图像最初是 BGR 格式。因此,在这一行中,我们将首先水平翻转图像以供稍后的自拍视图显示,然后将 BGR 图像转换为 RGB。图像可写标志设置为假。
图像 = cv2.cvtColor(cv2.flip(图像, 1), cv2.COLOR_BGR2RGB)
image.flags.writeable = False
之后,我们将通过手部跟踪模型传递图像以进行检测并将结果存储在一个名为“结果”的变量中。
结果=hands.process(图像)
检测完成后,我们将图像可写标志设置为 true,并将 RGB 图像转换为 BGR。
image.flags.writeable = True
图像 = cv2.cvtColor(图像,cv2.COLOR_RGB2BGR)
现在,当我们得到检测结果时,我们将调用mp_drawing 变量在图像上绘制这些检测,并使用我们之前导入的绘图实用程序连接所有检测。
如果结果.multi_hand_landmarks:
对于 results.multi_hand_landmarks 中的 hand_landmarks:
mp_drawing.draw_landmarks(
图片、hand_landmarks、mp_hands.HAND_CONNECTIONS)
之后,我们将调用findPosition()函数来获取所有检测的 id 和坐标。这些值将存储在名为 lmList 的变量中。
lmList = findPosition(图像,绘制=真)
现在我们有了所有手部标志的坐标,我们将使用它们来检测不同的手势,其中第一个是检测拳头是张开还是闭合。为此,我们将比较指尖 [8, 12, 16, 20] 和中点 [6, 10, 14, 19] 的坐标,如果指尖低于中点,则拳头闭合,反之亦然反之亦然。
对于范围内的 id(1, 5):
如果 lmList[tipIds[id]][2] < lmList[tipIds[id] - 2][2]:
手指.append(1)
如果(lmList[tipIds[id]][2] > lmList[tipIds[id] - 2][2]):
手指.append(0)
然后在接下来的几行中,获取计数的手指总数并将其保存在一个名为totalFingers的变量中。
totalFingers = Fingers.count(1)
打印(总手指)
现在我们得到了手指的数量,我们将使用它们来播放和暂停视频。
如果 totalFingers == 4:
状态 = “播放”
如果 totalFingers == 0 并且 state == "Play":
状态 = “暂停”
pyautogui.press('空格')
打印(“空间”)
然后我们要检测的下一个手势是向左、向右、向上和向下移动。为了检测左右移动,首先我们将获取食指尖的 X 坐标,如果值小于 300 则为左滑,如果值大于 400 则为右滑。
如果 totalFingers == 1:
如果 lmList[8][1]<300:
打印(“左”)
pyautogui.press('左')
如果 lmList[8][1]>400:
打印(“正确”)
pyautogui.press('对')
同理,检测上下手势,我们会得到中指的 Y 坐标,如果值小于 210 则为上滑,如果值大于 230 则为下滑.
如果 totalFingers == 2:
如果 lmList[9][2] < 210:
打印(“向上”)
pyautogui.press('向上')
如果 lmList[9][2] > 230:
打印(“向下”)
pyautogui.press('向下')
测试我们的手势控制媒体控制器脚本
现在媒体计数器脚本已经准备好了,让我们继续测试它。因此,将 Raspberry Pi 摄像头模块与 Pi 连接,如下所示:
现在,检查 Pi 相机是否正常工作。查看相机后,启动 Python 脚本,您会发现弹出一个窗口,其中包含视频源。现在,您可以通过手势控制视频播放器。您可以在下面找到该项目的工作视频。您可以将视频源更改为您选择的任何视频,并享受通过手势控制它的乐趣。
导入简历2
将媒体管道导入为 mp
导入pyautogui
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
##################################
tipIds = [4, 8, 12, 16, 20]
状态 = 无
手势 = 无
wCam, hCam = 720, 640
############################
def 手指位置(图像,handNo=0):
lmList = []
如果结果.multi_hand_landmarks:
myHand = results.multi_hand_landmarks[handNo]
对于 id, lm in enumerate(myHand.landmark):
# 打印(id,lm)
h, w, c = image.shape
cx, cy = int(lm.x * w), int(lm.y * h)
lmList.append([id, cx, cy])
返回 lmList
# 对于网络摄像头输入:
上限 = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
与 mp_hands.Hands(
min_detection_confidence=0.8,
min_tracking_confidence=0.5) 作为手牌:
而 cap.isOpened():
成功,图像 = cap.read()
如果不成功:
print("忽略空相机帧。")
# 如果加载视频,请使用“break”而不是“continue”。
继续
# 水平翻转图像以供稍后自拍视图显示,并转换
# 将 BGR 图像转为 RGB。
图像 = cv2.cvtColor(cv2.flip(图像, 1), cv2.COLOR_BGR2RGB)
image.flags.writeable = False
结果=hands.process(图像)
# 在图像上绘制手部注释。
image.flags.writeable = True
图像 = cv2.cvtColor(图像,cv2.COLOR_RGB2BGR)
如果结果.multi_hand_landmarks:
对于 results.multi_hand_landmarks 中的 hand_landmarks:
mp_drawing.draw_landmarks(
图片、hand_landmarks、mp_hands.HAND_CONNECTIONS)
lmList = 手指位置(图像)
#打印(lmList)
如果 len(lmList) != 0:
手指 = []
对于范围内的 id(1, 5):
如果 lmList[tipIds[id]][2] < lmList[tipIds[id] - 2][2]:
#state = "播放"
手指.append(1)
如果 (lmList[tipIds[id]][2] > lmList[tipIds[id] - 2][2] ):
# state = "暂停"
# pyautogui.press('空格')
# print("空格")
手指.append(0)
totalFingers = Fingers.count(1)
打印(总手指)
#print(lmList[9][2])
如果 totalFingers == 4:
状态 = “播放”
# finger.append(1)
如果 totalFingers == 0 并且 state == "Play":
状态 = “暂停”
pyautogui.press('空格')
打印(“空间”)
如果 totalFingers == 1:
如果 lmList[8][1]<300:
打印(“左”)
pyautogui.press('左')
如果 lmList[8][1]>400:
打印(“正确”)
pyautogui.press('对')
如果 totalFingers == 2:
如果 lmList[9][2] < 210:
打印(“向上”)
pyautogui.press('向上')
如果 lmList[9][2] > 230:
打印(“向下”)
pyautogui.press('向下')
#cv2.putText(image, str("Gesture"), (10,40), cv2.FONT_HERSHEY_SIMPLEX,
# 1, (255, 0, 0), 2)
cv2.imshow("媒体控制器", image)
键 = cv2.waitKey(1) & 0xFF
# 如果 `q` 键被按下,则退出循环
如果键 == ord("q"):
休息
cv2.destroyAllWindows()