mediapipe包实现简单的手部识别

前言

  一开始我还是java和c++的忠实用户,对python多少有点轻视,但很快就发现我错了,python没有java和c++那样严格的各种语法和格式要求,可以说是非常简易的。而且python有着大量的第三方库,可以轻轻松松用十几行代码实现各种各样的功能,代码写起来还是比c++和java轻松许多的。最近也在学习python里的一些第三方库,就写篇文章记录一下学习成果和笔记吧。

环境配置

  我的开发环境是python3.8+pycharm,这次主要用到的包就是mediapipeopcv-python,怎么下载这两个包我就不说了,如果有不会的可以自行百度。下面就先简单介绍一下这两个包吧,(只是简单介绍一下这些包在这个案例里要干什么以及可以干什么,具体的我也不是太了解了)。

  MediaPipe

  MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框。我们可以直接获取到谷歌已经训练好的识别模型,通过接口调用对图片或视频里的人进行手部、脸部或者身体骨架的识别,这些模型我们也可以使用自己的数据进行训练,这里不再多说。在这个案例里面我们要使用手部检测的模型。

  首先我们先看一下GitHub上这部分的文档上的示例代码。

#导入opencv-python的cv2模块
import cv2
#导入mediapipe模块
import mediapipe as mp
#获取绘制特征点的工具类
mp_drawing = mp.solutions.drawing_utils
#这个应该是设置绘制时的样式的
mp_drawing_styles = mp.solutions.drawing_styles
#获取手部识别模型的类
mp_hands = mp.solutions.hands
#创建一个识别模型类的对象供我们使用
hands=mp_hands.Hands()#创建的时候可以设置许多参数,这里我就采用默认值

  mp.solutions里面有各种我们可能用到的类,比如脸部识别的,手部识别,绘制特征点的等等,需要什么就去这里面拿就行了。

  接着就是用我们的模型去识别图片或者视频。

#使用该对象身上的process方法传进来一个RGB格式的图片,会把识别结果进行返回
results = hands.process(img)
#results身上的multi_hand_landmarks会存放是否检测到该图形中有手,如果检测到手的检测信息存放在里面
if results.multi_hand_landmarks:
        #遍历每只手
        for hmLandmarks in results.multi_hand_landmarks:
            #遍历每只手上的特征点
            for idx,f in enumerate(hmLandmarks.landmark):
                print(idx,f)

  每只手上都有21个监测点,每个点都有对应的坐标信息,但这些坐标是已经进行归一化处理的,我们需要乘上窗口的大小获得实际的坐标值。下面是在网上找到的手部标志点的图片,开发时可以参考这个。

 opencv-python

  opencv搞过计算机图形学或者人工智能方面的应该都不陌生,经常用来进行图像处理,我了解也不是特别深入,就主要说一下这个案例里面我们怎么用到的这个包。如果我们想实时的用我们摄像头对我们的手进行识别,需要通过这个包开启摄像头拍照,然后再将每一帧的图片用mediapipe的模型进行识别再把识别结果输出即可。

import cv2
#获取摄像头,0为默认计算机默认摄像头,1可以更换来源
cap = cv2.VideoCapture(0)

while True:
    #不断的获取当前的图片,返回获取的状态和获取到的图片(图片是BGR格式的)
    sat,img=cap.read();
    
    #将图片显示到窗口
    cv2.imshow("Image",img)
    #等待一毫秒,这样才能看到拍摄到的图片
    cv2.waitKey(1)

正式开始 

  准备工作都做完之后,我们就可以正式开始工作了,下面先把全部的代码和运行结果展示出来。

import cv2
import mediapipe as mp
import time

cap=cv2.VideoCapture(0)

mpHands = mp.solutions.hands
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils

#计算帧率
pTime=0
cTime=0
while True:
    success,img=cap.read()
    #因为我们获取到的图像是GBR的,所以要转换成RGB格式再交给模型处理
    imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    #result里面存放的是我们每次的检测结果
    result=hands.process(imgRGB)
    #print(result.multi_hand_landmarks)

    #如果画面检测到了手就绘制手上的点
    if result.multi_hand_landmarks:
        for handLms in result.multi_hand_landmarks:#可能检测到了多个手,遍历这些手
            for id,lm in enumerate(handLms.landmark):#每个手上都有21个点的信息,遍历每个手上的所有点的信息
                h,w,c=img.shape#获取窗口的高和宽
                cx,cy=int(lm.x*w),int(lm.y*h)#算出这个点的中心位置
                #在我们的手腕出画一个圆
                if id==0:
                    cv2.circle(img,(cx,cy),15,(40,123,222),cv2.FILLED)#在这个点的中心位置画圆
            #使用绘制工具将所有的点连接起来
            mpDraw.draw_landmarks(img,handLms,mpHands.HAND_CONNECTIONS)#绘制手上的点并将点用线连起来
    #计算帧率
    cTime=time.time()
    fps=1/(cTime-pTime)
    pTime=cTime
    #将帧率输出到窗口上
    cv2.putText(img,str(int(fps)),(10,70),cv2.FONT_HERSHEY_PLAIN,3,(40,123,222),3)

    cv2.imshow("Image",img)
    cv2.waitKey(1)

  这样我们就可以得到手部的所有特征点的位置信息了,有了这些信息,我们就能够做各种各样的事情了,比如手势识别、通过手指的移动操作鼠标、手势控制电脑音量等等,后面再具体介绍吧。

  为了以后方便使用,我们把这些代码封装起来,供以后调用方便。

import cv2
import mediapipe as mp
import time


class HandDetector():
    #初始化一些必要参数
    def __init__(self,mode=False,maxHands=2,modelComplexity=1,detection=0.75,tracking=0.5):
        self.mode=mode
        self.maxHands=maxHands
        self.modmodelComplexitye=modelComplexity
        self.detection=detection
        self.tracking=tracking
        self.mpHands = mp.solutions.hands
        self.hands = self.mpHands.Hands(mode,maxHands,modelComplexity,detection,tracking)
        self.mpDraw = mp.solutions.drawing_utils
        self.fingerPoints = [4, 8, 12, 16, 20]  # 每根手指的第一个点的编号
    
    #对图像进行识别检测
    def findHand(self,img,draw=True):
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.result = self.hands.process(imgRGB)
        if self.result.multi_hand_landmarks:
            for handLms in self.result.multi_hand_landmarks:  # 每一个手上有多个点,这些点信息放在handLms里
                if draw:
                    self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS)  # 绘制手上的点并将点用线连起来

        return img
    
    #将一个手上的所有点的坐标和id放到数组里返回
    def getPosition(self,img):
        handList=[]
        if self.result.multi_hand_landmarks:
            for handLms in self.result.multi_hand_landmarks:
                for id, lm in enumerate(handLms.landmark):  # 遍历每个手上的所有点的信息
                    h, w, c = img.shape  # 获取窗口的高和宽
                    cx, cy = int(lm.x * w), int(lm.y * h)  # 算出这个点的中心位置
                    handList.append([id, cx, cy])
        return handList

    #根据传进来的id值,给对应点的位置画一个圆
    def drawPosition(self,img,handList,ids):
        if len(handList)>0:
            for point in handList:
                if point[0]in ids:
                    id=point[0]
                    cv2.circle(img, (handList[id][1], handList[id][2]), 10, (40, 123, 222), cv2.FILLED)  # 在这个点的中心位置画圆

def main():
    pTime = 0
    cap = cv2.VideoCapture(0)
    detector = HandDetector()
    while True:
        success, img = cap.read()
        img=detector.findHand(img)
        handList=detector.getPosition(img)

        cTime = time.time()
        fps = 1 / (cTime - pTime)
        pTime = cTime
        cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (40, 123, 222), 3)

        cv2.imshow("Image", img)
        cv2.waitKey(1)

if __name__=="__main__":
    main()

 

posted @ 2023-11-17 15:03  小明同学404  阅读(304)  评论(0编辑  收藏  举报