使用Python、OpenCv和MediaPipe创建一个手部跟踪模块
手部追踪是指计算机使用计算机视觉从输入的图像中检测出一只手,并保持对手的运动和方向的关注。手部追踪使我们能够开发出许多以手部运动和方向为输入的程序。
我们倾向于在不同的项目中编写相同的代码,将手部跟踪作为程序的一部分来执行。创建一个手部跟踪模块可以解决这个问题,因为我们只写一次代码。
然后我们将这段代码转换成一个模块。我们可以把这个模块导入到我们正在进行的任何一个Python项目中,它就会执行手部跟踪。
简介
为了创建执行手部跟踪的程序,我们需要两个Python库。这两个库是openCV 和MediaPipe 。
我们将使用openCV 来执行与计算机视觉相关的操作。我们将使用MediaPipe 来对我们的输入图像进行实际的手部检测和跟踪。我们最后还需要一个IDE。在本教程中,我们将使用Pycharm IDE。
本教程将分为两部分。第一部分将重点介绍如何创建一个进行手部追踪的程序。第二部分将重点介绍如何将程序变成一个模块。使用Windows、Linux或macOS的人可以进行跟踪。
前提条件
要跟上本教程,你应该。
- 熟悉Python编程语言。
- 在你的计算机上安装了
PycharmIDE。
设置我们的环境
启动Pycharm 应用程序,并进行以下操作。
- 在显示的第一个窗口中点击
create a new project。这在下面的屏幕截图中显示。
![]()
- 在接下来出现的窗口中,点击创建。
- 安装我们讨论过的两个python库。要做到这一点,打开终端,如下图所示,然后按照下面的步骤操作。
![]()
- 在终端键入以下命令来安装
MediaPipe。
pip install mediapipe
- 要安装
openCV,使用下面的命令。
pip install opencv-python
现在我们的环境已经准备好了。我们将开始创建一个可以进行手部追踪的程序。
创建一个手部跟踪程序
在我们开始编码之前,让我们讨论一下MediaPipe 如何进行手部跟踪。使用MediaPipe 进行手部跟踪包括两个阶段。
- 手掌检测-
MediaPipe在完整的输入图像上工作,并提供手部的剪裁图像。 - 手部地标识别--
MediaPipe在裁剪后的手部图像上找到21的手部地标。
MediaPipe 识别的21 手部点显示在下面的图片中。
![]()
上面的图片显示了MediaPipe用来识别手的地标。编号的部分是手部的点。
编码
在你创建一个新项目后,Pycharm会自动为你创建一个main.py 文件。这就是我们要写代码的地方。
第1步 - 导入和初始化
我们首先要导入我们讨论过的两个库。导入库使我们能够使用它的依赖性。
然后我们将创建一个用于视频捕捉的对象cap 。我们需要其他三个对象来操作我们的输入,使用MediaPipe 。
import cv2
import mediapipe as mp
cap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils
第2步 - 捕获图像输入并处理它
下面的代码从网络摄像头获取图像输入。然后它将图像从BGR 转换成RGB 。这是因为MediaPipe 只对RGB 的图像起作用,而不是BGR 。
然后,它处理RGB 图像以识别图像中的手。
while True:
success, image = cap.read()
imageRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = hands.process(imageRGB)
第3步 - 处理每只手
# checking whether a hand is detected
if results.multi_hand_landmarks:
for handLms in results.multi_hand_landmarks: # working with each hand
for id, lm in enumerate(handLms.landmark):
h, w, c = image.shape
cx, cy = int(lm.x * w), int(lm.y * h)
在上面的代码中,我们使用if 语句来检查是否检测到一只手。然后我们使用第一个for 循环,使我们能够一次处理一只手。
第二个for 循环帮助我们获得手的地标信息,这将给我们提供手的地标图中每个列出的点的坐标x 和y 。这个循环也会给我们每个点的id 。
然后,我们将使用image.shape 函数找到我们图像的height 、width 和channel 。我们最终得到确定的手部点的中心位置。
第4步--在手部图像上绘制手部地标和手部连接点
if id == 20 :
cv2.circle(image, (cx, cy), 25, (255, 0, 255), cv2.FILLED)
mpDraw.draw_landmarks(image, handLms, mpHands.HAND_CONNECTIONS)
在上面的代码中,我们圈出了编号为20 的手部点。这是小拇指的尖端。
请随意使用你想圈出的手部点的编号,因为它们都列在手部地标图上。然后我们在输入图像上画出手部地标和它们之间的联系。
第5步 - 显示输出
cv2.imshow("Output", image)
cv2.waitKey(1)
我们使用上面的代码来向用户显示输出。该输出是用户的实时视频。它有用户的手被跟踪,手的地标,以及在手上画的连接。
从我们的代码中创建一个模块
创建一个新的文件,并将其命名为handTrackingModule 。你可以自由地给它起任何名字。现在让我们按照下面的步骤从上面的代码创建一个模块。
第1步 - 导入所需的库
我们首先导入我们的项目中需要的Python 库。这将使我们能够使用其依赖性。
import cv2
import mediapipe as mp
第2步 - 创建一个我们将用于手部检测的类
class handTracker():
def __init__(self, mode=False, maxHands=2, detectionCon=0.5,modelComplexity=1,trackCon=0.5):
self.mode = mode
self.maxHands = maxHands
self.detectionCon = detectionCon
self.modelComplex = modelComplexity
self.trackCon = trackCon
self.mpHands = mp.solutions.hands
self.hands = self.mpHands.Hands(self.mode, self.maxHands,self.modelComplex,
self.detectionCon, self.trackCon)
self.mpDraw = mp.solutions.drawing_utils
在上面的代码中,我们创建了一个我们将用于跟踪的类。然后,我们键入基本参数,这些参数是hands 功能工作所需的。MediaPipe在hands函数中提供这些参数。
之后,我们为我们的类提供所有需要的初始化。这些是上面的参数和MediaPipe 的初始化。
我们在每个对象前都放上self ,以允许对该对象的方法和属性进行访问。这反过来又允许每个对象拥有自己的属性和方法。
第3步 - 创建一个方法来跟踪我们输入图像中的手
def handsFinder(self,image,draw=True):
imageRGB = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
self.results = self.hands.process(imageRGB)
if self.results.multi_hand_landmarks:
for handLms in self.results.multi_hand_landmarks:
if draw:
self.mpDraw.draw_landmarks(image, handLms, self.mpHands.HAND_CONNECTIONS)
return image
在上面的代码中,我们创建了一个方法,我们将用它来专门跟踪我们输入图像中的手。这个方法中的代码是将图像转换为RGB ,并处理RGB 图像以定位手。
它还在图像上画出手的地标,最后画出手的连接。
第4步--创建一个方法来寻找每个手点的'x'和'y'坐标
def positionFinder(self,image, handNo=0, draw=True):
lmlist = []
if self.results.multi_hand_landmarks:
Hand = self.results.multi_hand_landmarks[handNo]
for id, lm in enumerate(Hand.landmark):
h,w,c = image.shape
cx,cy = int(lm.x*w), int(lm.y*h)
lmlist.append([id,cx,cy])
if draw:
cv2.circle(image,(cx,cy), 15 , (255,0,255), cv2.FILLED)
return lmlist
在上面的代码中,我们创建了一个方法,我们将用它来寻找21个手部点中每个点的x 和y 坐标。我们还创建了一个列表,用来存储这些坐标的值。
这个方法中的代码是我们用来寻找每个手点的ID和手标信息的。我们还输入了代码,用来圈出我们要使用的手点。
第5步 - 创建主方法
def main():
cap = cv2.VideoCapture(0)
tracker = handTracker()
while True:
success,image = cap.read()
image = tracker.handsFinder(image)
lmList = tracker.positionFinder(image)
if len(lmList) != 0:
print(lmList[4])
cv2.imshow("Video",image)
cv2.waitKey(1)
上面的代码代表了我们将用来展示该模块能做什么的假代码。在我们的例子中,它可以识别和跟踪手。它使用image 和lmlist 对象。这段代码出现在主方法中。
第6步 - 执行主方法
if __name__ == "__main__":
main()
这段代码意味着,如果我们正在运行模块脚本,然后执行主方法。
结果
程序和模块的输出将是相同的。当它们各自运行完成而没有任何错误时,输出结果将如下所示。
![]()
结论
你现在了解并掌握了创建一个执行手部跟踪的程序所需的所有技能。你也具备了将代码转换为模块所需的技能。
去吧,把这个模块导入任何需要手部跟踪的Python 项目中,看这个模块发挥它的魔力。