如何使用Python、OpenCv和MediaPipe创建一个手部跟踪模块

682 阅读7分钟

使用Python、OpenCv和MediaPipe创建一个手部跟踪模块

手部追踪是指计算机使用计算机视觉从输入的图像中检测出一只手,并保持对手的运动和方向的关注。手部追踪使我们能够开发出许多以手部运动和方向为输入的程序。

我们倾向于在不同的项目中编写相同的代码,将手部跟踪作为程序的一部分来执行。创建一个手部跟踪模块可以解决这个问题,因为我们只写一次代码。

然后我们将这段代码转换成一个模块。我们可以把这个模块导入到我们正在进行的任何一个Python项目中,它就会执行手部跟踪。

简介

为了创建执行手部跟踪的程序,我们需要两个Python库。这两个库是openCVMediaPipe

我们将使用openCV 来执行与计算机视觉相关的操作。我们将使用MediaPipe 来对我们的输入图像进行实际的手部检测和跟踪。我们最后还需要一个IDE。在本教程中,我们将使用Pycharm IDE。

本教程将分为两部分。第一部分将重点介绍如何创建一个进行手部追踪的程序。第二部分将重点介绍如何将程序变成一个模块。使用Windows、Linux或macOS的人可以进行跟踪。

前提条件

要跟上本教程,你应该。

  • 熟悉Python编程语言。
  • 在你的计算机上安装了Pycharm IDE。

设置我们的环境

启动Pycharm 应用程序,并进行以下操作。

  1. 在显示的第一个窗口中点击create a new project 。这在下面的屏幕截图中显示。

Project

  1. 在接下来出现的窗口中,点击创建。
  2. 安装我们讨论过的两个python库。要做到这一点,打开终端,如下图所示,然后按照下面的步骤操作。

Installation

  • 在终端键入以下命令来安装MediaPipe
pip install mediapipe
  • 要安装openCV ,使用下面的命令。
pip install opencv-python

现在我们的环境已经准备好了。我们将开始创建一个可以进行手部追踪的程序。

创建一个手部跟踪程序

在我们开始编码之前,让我们讨论一下MediaPipe 如何进行手部跟踪。使用MediaPipe 进行手部跟踪包括两个阶段。

  • 手掌检测-MediaPipe 在完整的输入图像上工作,并提供手部的剪裁图像。
  • 手部地标识别--MediaPipe 在裁剪后的手部图像上找到21 的手部地标。

MediaPipe 识别的21 手部点显示在下面的图片中。

Hand landmark model

上面的图片显示了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 循环帮助我们获得手的地标信息,这将给我们提供手的地标图中每个列出的点的坐标xy 。这个循环也会给我们每个点的id

然后,我们将使用image.shape 函数找到我们图像的heightwidthchannel 。我们最终得到确定的手部点的中心位置。

第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个手部点中每个点的xy 坐标。我们还创建了一个列表,用来存储这些坐标的值。

这个方法中的代码是我们用来寻找每个手点的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)

上面的代码代表了我们将用来展示该模块能做什么的假代码。在我们的例子中,它可以识别和跟踪手。它使用imagelmlist 对象。这段代码出现在主方法中。

第6步 - 执行主方法

if __name__ == "__main__":
    main()

这段代码意味着,如果我们正在运行模块脚本,然后执行主方法。

结果

程序和模块的输出将是相同的。当它们各自运行完成而没有任何错误时,输出结果将如下所示。

Results

结论

你现在了解并掌握了创建一个执行手部跟踪的程序所需的所有技能。你也具备了将代码转换为模块所需的技能。

去吧,把这个模块导入任何需要手部跟踪的Python 项目中,看这个模块发挥它的魔力。