使用Python的MediaPipe HandPose检测
手势识别是一种深度学习技术,它允许你检测你手上的不同点。你手上的这些点通常被称为地标。这些地标包括手指的关节、尖端和基部。
MediaPipe提供许多可定制的ML预训练模型。handpose模型是他们最新发布的模型之一。随着研究人员旨在利用这种惊人的预训练模型使人工智能民主化,我们只需几行代码就能理解ML,这一点很重要。
本教程旨在向你展示如何使用MediaPipe和Python建立你自己的Handpose检测器。你将能够使用你的电脑的网络摄像头来跟踪你的手的关节。
前提条件
要跟上本教程,你需要熟悉以下内容。
- 机器学习建模。
- Jupyter笔记本/谷歌Colab。
我们将在本教程中使用谷歌Colab。
手势模型
handpose模型是由tensorflow.js提供的,可以检测到你手上的21个不同的不同点。MediaPipe Hands模型是一个轻量级的ML管道,由一个手掌检测器和一个手掌骨架手指跟踪模型组成。
最初,手掌检测器检测手的位置,之后,手骨架手指跟踪模型进行精确的关键点定位,预测每个检测到的手的21个3D手的关键点。
让我们看看如何在一个项目中使用这个手骨架模型。
安装和导入依赖项
我们对两个核心依赖项进行快速的pip安装;MediaPipe和openCV Python库。MediaPipe是一个开源的、跨平台的库,为解决计算机视觉问题提供了许多现成的ML解决方案。一些例子包括人脸检测、自拍分割、头发分割和物体检测的ML解决方案。在本教程中,我们利用该库在我们的项目中导入MediaPipe Hands模型。
我们还将安装OpenCV库。与MediaPipe一样,OpenCV也是一个有助于解决计算机视觉问题的库。在本教程中,我们将使用该库来处理图像,并轻松地实时访问我们的网络摄像头。
!pip install mediapipe opencv-python
接下来,我们将把必要的依赖项导入我们的笔记本。
import mediapipe as mp
import cv2
import numpy as np
import uuid
import os
from google.colab.patches import cv2_imshow
我们已经导入了五个依赖项。
mediapipe使我们能够利用MediaPipe ML解决方案。cv2为我们提供OpenCV。numpy让我们更容易处理数字输出。uuid允许你生成一个统一的唯一标识符。os允许我们在操作系统中与文件一起工作。
我们现在可以设置mediapipe了。让我们引入mediapipe的手部模型和绘图工具,帮助我们画出手上的所有地标。
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
现在所有这些都完成了,让我们使用标准的OpenCV代码来设置我们的网络摄像头。
img = cv2.imread('logo.png', cv2.IMREAD_UNCHANGED)
cv2_imshow(img)
在Google Colab中使用OpenCV访问你的网络摄像头并不是很直接,因为你不是在使用你的本地运行时,而是在使用Google Colab运行时。
为了在虚拟机中利用你的本地机器的网络摄像头,你可以复制粘贴以下JavaScript代码到你的Colab中。
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode
def take_photo(filename='photo.jpg', quality=0.8):
js = Javascript('''
async function takePhoto(quality) {
const div = document.createElement('div');
const capture = document.createElement('button');
capture.textContent = 'Capture';
div.appendChild(capture);
const video = document.createElement('video');
video.style.display = 'block';
const stream = await navigator.mediaDevices.getUserMedia({video: true});
document.body.appendChild(div);
div.appendChild(video);
video.srcObject = stream;
await video.play();
// Resize the output to fit the video element.
google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
// Wait for Capture to be clicked.
await new Promise((resolve) => capture.onclick = resolve);
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0);
stream.getVideoTracks()[0].stop();
div.remove();
return canvas.toDataURL('image/jpeg', quality);
}
''')
display(js)
data = eval_js('takePhoto({})'.format(quality))
binary = b64decode(data.split(',')[1])
with open(filename, 'wb') as f:
f.write(binary)
return filename
这段代码是由谷歌的团队预先建立的,以帮助开发人员更容易访问他们的网络摄像头。
此外,还可以复制粘贴下面的代码。运行这段代码将打开你电脑的网络摄像头。一旦它启动,你就可以捕捉到自己的图像。记住要用你的一只手来捕捉你的图像,因为这个模型的目的是捕捉手部姿势。如果你不捕捉你的手,你将不会看到任何结果。该图像将被保存为photo.jpg 。
from IPython.display import Image
try:
filename = take_photo()
print('Saved to {}'.format(filename))
# Show the image which was just taken.
display(Image(filename))
except Exception as err:
# Errors will be thrown if the user does not have a webcam or if they do not
# grant the page permission to access it.
print(str(err))
现在让我们把mediapipe的手部模型覆盖在标准的OpenCV代码之上。我们将从我们的网络摄像头获取信息,将其传递给mediapipe,进行检测,并将结果渲染到图像上。因此,我们不仅会得到网络摄像头的反馈,而且会得到网络摄像头的反馈,并将所有这些检测结果应用于其中。
使用网络摄像头拍摄的图像来检测手势
在我们将手放在摄像头前的捕捉到的图像中,我们应该看到我们手上的所有关节都被检测到了。
cap = cv2.imread('photo.jpg', cv2.IMREAD_UNCHANGED)
with mp_hands.Hands(min_detection_confidence=0.8, min_tracking_confidence=0.5) as hands: #You can pass `max_num_hands` argument here as well if you want to detect more that one hand
image = cv2.cvtColor(cap, cv2.COLOR_BGR2RGB)
image.flags.writeable = False
results = hands.process(image)
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
print(results)
# Rendering results
if results.multi_hand_landmarks:
for num, hand in enumerate(results.multi_hand_landmarks):
mp_drawing.draw_landmarks(image, hand, mp_hands.HAND_CONNECTIONS,
)
cv2.imwrite(os.path.join('Output Images', '{}.jpg'.format(uuid.uuid1())), image)
cv2_imshow(image)
cv2.destroyAllWindows()
我们首先使用以下代码实例化我们的模型with mp_hands.Hands(min_detection_confidence=0.8, min_tracking_confidence=0.5) as hands 。我们传入两个关键字参数,min_detection_confidence ,将检测置信度设置为80%,min_tracking_confidence ,将跟踪置信度设置为50%。
然后,我们使用OpenCV的cvtColor 方法将我们的框架从BGR 重新着色到RGB 。默认情况下,OpenCV将图像颜色的格式设置为BGR 。我们需要将其设置为RGB ,因为那是mediapipe接受的格式。我们将这个结果存储在一个叫做image 的变量中。
我们将我们的可写标志,image.flags.writeable 最初设置为false ,以避免在我们进行检测后再将其设置为true 。代码hands.process(image) 继续进行我们的检测,并将其存储在一个被称为results 的变量中。
下一步是使用cvtColor 方法。我们将图像的颜色从RGB 设置回BGR ,并打印我们的检测结果。在这一点上,我们的网络摄像头图像没有发生任何变化。如果我们在终端上输入results.multi_hand_landmarks ,我们应该看到手势检测结果。我们需要将这些检测结果渲染到我们的图像上。
代码的最后一点帮助我们将这些结果渲染到我们的图像上。如果我们在multi_hand_landmarks 中有结果,就渲染图像,如果没有,就不渲染。然后,我们循环浏览每一组结果,并绘制地标。mp_hands.HAND_CONNECTIONS ,告诉我们关系的集合;哪些地标与哪些地标相连,让我们绘制连接。
默认情况下,mediapipe检测到的最多是两只手。如果你想检测一个以上的人的手,你必须引入一个参数,max_num_hands ,并指出你想检测的手的数量。它默认设置为两只,以减少延迟,因为如果没有其他的手要被检测,就没有必要调用另一个检测。
这就是了。如果你运行网络摄像头画面并展示你的手,你应该在你的手指上有地标检测。
使用OpenCV输出图像
最后,如果你想保存检测的结果,也许是为了研究论文或个人使用,你可以通过添加以下代码来实现。
cv2.imwrite(os.path.join('Output Images', '{}.jpg'.format(uuid.uuid1())), image)
上面这行代码是要保存我们的图像。唯一的标识符,format(uuid.uuid1() ,为我们检测到的图像生成唯一的名称,以避免在保存图像时发生命名冲突。
收尾工作
我们从安装和导入我们的依赖项开始,我们从我们的网络摄像头进行检测,并将这些检测应用于我们的网络摄像头馈送。最后一步是保存我们的输出。你可以自己去试试。