【openCV+mediapipe+yolo项目集】1.手部跟踪小项目以及手部跟踪模块的创建

一.项目效果以及目的

实现如下图的手部识别即跟踪的效果,并且做成一个工具包,方便以后做其他项目时候直接使用其中的API

二.手部跟踪

1.所用到的包

cv2,mediapipe,time

使用pip install 或者在anaconda中下载即可

import cv2

import mediapipe as mp

import time

 

2.思路

用到opencv的地方不再多写,主要是记录mediapipe的用法

首先利用opencv调用电脑摄像头并且将其显示出来,再用mediapipe中的API进行手部识别与跟踪即可,具体用到的API如下

要进行手部跟踪,首先需要创建一个对象(如上文所示,吧mediapipe简称为mp,因此代码中的所有mp都是mediapipe)

mpHands = mp.solutions.hands

#四个参数,都有缺省值

#第一个参数为static_image_mode = False,当为True的时候一直是图像检测模式,

#当为False的时候,只在最开始的时候检测手部在哪里,之后只需要跟踪手部即可,当跟踪的过程中发现这个东西为手部的置信度小于某一个值的时候

#就会重新检测手部在哪里,继续跟踪.设置为False是比较节省资源的,因此使用它的缺省参数即可

#第二个参数为max_num_hands = 2,为能同时检测的手部的最大值,这里我们也用2即可

#第三个和第四个参数为置信度阈值,默认为0.5

hands = mpHands.Hands()

 

如注释所示,在老版本中,Hands()有四个参数,但是新版本增加到了五个,如下图所示:

大概的工作流程就是,先检测手在哪里,并且检测到手的数量不能超过传入的参数max_num_hands设置的数量.然后如果第一个参数为False,就会进入跟踪模式,在此期间如果跟踪的东西为手的置信度小于传入的第四个和第五个参数的值,就会重新检测手在哪里,否则就一直跟踪.这种方法比较节省资源.如果第一个参数设置为True,就会每一帧每一帧一直检测手在哪里,会浪费资源并且速度变慢

接下来调用这个对象的process方法即可

results = hands.process(imgRGB)

其中imgRGB是要进行处理的图片,它的通道格式移动式RGB的,但是opencv获取的图像默认是BGR的,因此还需要调用opencv的方法将其转换成RGB

results作为process方法的返回值,它有三个参数,都是列表的形式,记录手中21个关键节点的坐标的,其形式都差不多,用哪个都大致相同,这里利用的是multi_hand_landmarks

可以打印以下看看它到底是个什么样的形式:

print(results.multi_hand_landmarks)

如图,如果检测到手,就会显示其坐标,没有就会显示none,因此他还能当一个bool值来判断图像中有没有手

它的shape实际是一个列表套一个字典,最外面那一层[]里面存放的是一个手,因为一个屏幕中可能会出现多只手.在[]内部存21个关键点的坐标,{}里面存放的每一个点的具体坐标,

因此,首先用一层for循环遍历results.multi_hand_landmarks里面的所有手,再分别对每只手进行绘制或者获取坐标的操作

if results.multi_hand_landmarks:

    #实际上muliti_hand_landmars返回的是一个列表,有多只手的信息,因此要写进for循环里绘制每一只手

    for handLms in results.multi_hand_landmarks:

        #要在原图BGR中绘制,因为opencv最终显示的是原图,加入第三个参数会连线,不加只会显示点

        pass

 

有了21个点的坐标,可以用opencv绘制每个点,并且将点之间连线,但是太麻烦了,因此mediapipe直接给出了绘制手部21个关键点和连线的方法,首先还是创建一个对象

mpDraw = mp.solutions.drawing_utils

再调用这个独享的draw_landmarks方法即可,其中handLms是上面for循环中写的一个变量,而mpHands.HAND_CONNECTIONS写上就会给点之间连线,不写就只会绘制21个点,其中mpHands是上文自己取得变量的名字

mpDraw.draw_landmarks(fream,handLms,mpHands.HAND_CONNECTIONS)

这样就可以绘制出手部的关键点以及连线了,但是有时候还会用到手部某个关键点的坐标,因此还需要如下的知识:

handLms(自己定义的变量名)中还有一个参数,为landmark,是一个列表,列表中每一项又有两个参数,分别将其命名为id和lm,其中id为21个关键点的标号,从0到20,lm是该关键点的坐标

其中21个点的标号(id)和它所对应的位置如图所示

if results.multi_hand_landmarks:

    #实际上muliti_hand_landmars返回的是一个列表,有多只手的信息,因此要写进for循环里绘制每一只手

    for handLms in results.multi_hand_landmarks:

        #分开打印每一个关节点的坐标

        for id,lm in enumerate(handLms.landmark):

            #两个参数分别是关节点在列表中的id和其坐标

            print(id,lm)

 

打印结果如图所示:

可以看到,坐标都是归一化的,也就是0到1之间的,因此要获得真正的坐标,还得在此基础上乘上长或者宽即可

综上,完整的获取关键点的坐标和绘制图像的代码如下:

"""当检测到屏幕内有手的时候,绘制手部图像"""

if results.multi_hand_landmarks:

    #实际上muliti_hand_landmars返回的是一个列表,有多只手的信息,因此要写进for循环里绘制每一只手

    for handLms in results.multi_hand_landmarks:

        #分开打印每一个关节点的坐标

        for id,lm in enumerate(handLms.landmark):

            #两个参数分别是关节点在列表中的id和其坐标

            print(id,lm)

            #返回的坐标都是0到1之间的,因此要过的真实的坐标,还需要乘以图片的长和宽

            h,w,c = fream.shape

            cx,cy = int(lm.x*w),int(lm.y*h)

            #得到了cx和cy就可以单独对某一个关节点进行操作了

        #要在原图BGR中绘制,因为opencv最终显示的是原图,加入第三个参数会连线,不加只会显示点

        mpDraw.draw_landmarks(fream,handLms,mpHands.HAND_CONNECTIONS)

 

此外还希望计算视频的帧率,并且显示在屏幕上,帧率的计算方式如下:

1/(这一帧的时间-上一帧的时间)

3.完整代码:

import cv2

import mediapipe as mp

import time


"""创建mediapipe的首部检测对象"""

#mediapipe有两种检测模式,一种是hand,也就是21个关键节点的手部检测

mpHands = mp.solutions.hands

#四个参数,都有缺省值

#第一个参数为static_image_mode = False,当为True的时候一直是图像检测模式,

#当为False的时候,只在最开始的时候检测手部在哪里,之后只需要跟踪手部即可,当跟踪的过程中发现这个东西为手部的置信度小于某一个值的时候

#就会重新检测手部在哪里,继续跟踪.设置为False是比较节省资源的,因此使用它的缺省参数即可

#第二个参数为max_num_hands = 2,为能同时检测的手部的最大值,这里我们也用2即可

#第三个和第四个参数为置信度阈值,默认为0.5

hands = mpHands.Hands()


"""由于检测到的手部有21个点,用opencv画出来是十分复杂的,因此mediapipe给提供了现成的方法,和上面一样也是先创建对象"""

mpDraw = mp.solutions.drawing_utils


"""计算fps用到的参数"""

pTime = 0#上一次的时间

cTime = 0#当前时间    现在的的时间减去上一帧的时间是一帧所用的时间,再除以1就是fps


"""利用opencv调用摄像头"""

cap = cv2.VideoCapture(0)


while True:

    ret,fream = cap.read()

    """mediapipe的Hands类只能处理RGB图像,因此先用openCV转换以下,再进行处理"""

    imgRGB = cv2.cvtColor(fream,cv2.COLOR_BGR2RGB)

    results = hands.process(imgRGB)

    #这个方法,如果图片内检测到手,会返回True并却返回坐标,否则返回none

    #print(results.multi_hand_landmarks)

    """当检测到屏幕内有手的时候,绘制手部图像"""

    if results.multi_hand_landmarks:

        #实际上muliti_hand_landmars返回的是一个列表,有多只手的信息,因此要写进for循环里绘制每一只手

        for handLms in results.multi_hand_landmarks:

            #分开打印每一个关节点的坐标

            for id,lm in enumerate(handLms.landmark):

                #两个参数分别是关节点在列表中的id和其坐标

                print(id,lm)

                #返回的坐标都是0到1之间的,因此要过的真实的坐标,还需要乘以图片的长和宽

                h,w,c = fream.shape

                cx,cy = int(lm.x*w),int(lm.y*h)

                #得到了cx和cy就可以单独对某一个关节点进行操作了

            #要在原图BGR中绘制,因为opencv最终显示的是原图,加入第三个参数会连线,不加只会显示点

            mpDraw.draw_landmarks(fream,handLms,mpHands.HAND_CONNECTIONS)


    """计算fps"""

    cTime = time.time()#当前时间

    fps = 1/(cTime - pTime)

    pTime = cTime

    """显示fps"""

    cv2.putText(fream,"FPS:"+str(int(fps)),(10,70),cv2.FONT_HERSHEY_SIMPLEX,2,(0,255,0),2)


    cv2.imshow("show",fream)

    key = cv2.waitKey(1)

    if key == 27:

        break;

cap.release()

cv2.destroyAllWindows()

 

三.把上述代码改成一个工具包

需要改的地方不多,只不过把一些功能写到一个类里面,提供两个方法,一个是寻找手部的代码,另一个是查询坐标的代码,具体细节看注释即可

import cv2

import mediapipe as mp

import time



"""手部查找器"""

class handDetector():


    """五个参数分别为创建Hands所需要的五个参数,直接从Hands.py中复制过来即可,缺省值不用变"""

    def __init__(self,static_image_mode = False,

        max_num_hands = 2,

        model_complexity = 1,

        min_detection_confidence = 0.5,

        min_tracking_confidence = 0.5):

        self.mode = static_image_mode

        self.max_num = max_num_hands

        self.model_complexity = model_complexity

        self.min_de = min_detection_confidence

        self.min_track = min_tracking_confidence


        self.mpHands = mp.solutions.hands

        self.hands = self.mpHands.Hands(self.mode,self.max_num,self.model_complexity,self.min_de,self.min_track)

        self.mpDraw = mp.solutions.drawing_utils


    """fream为原图,draw为True的时候绘制关键点和骨架,为False的时候不绘制"""

    """返回值是经过处理后的图像,实际上第二个参数为False的时候没有意义"""

    def findHands(self,fream,draw = True):

        imgRGB = cv2.cvtColor(fream, cv2.COLOR_BGR2RGB)

        self.results = self.hands.process(imgRGB)

        if self.results.multi_hand_landmarks:

            for handLms in self.results.multi_hand_landmarks:

                if draw:

                    self.mpDraw.draw_landmarks(fream, handLms, self.mpHands.HAND_CONNECTIONS)

        return fream


    """fream为原图,handNo为需要返回第几个手的坐标,draw=True的时候,会给21个关键点标上更粗的紫色圆圈,draw没实际用处,因此默认为False"""

    """返回值为一个列表,存放的是这只手21个点的坐标,可以用索引分别查询每一个点的坐标"""

    def findPositon(self,fream,handNo=0,draw=False):


        lmList = []


        if self.results.multi_hand_landmarks:

            myHand = self.results.multi_hand_landmarks[handNo]

            for id, lm in enumerate(myHand.landmark):

                h, w, c = fream.shape

                cx, cy = int(lm.x * w), int(lm.y * h)

                lmList.append([id,cx,cy])

                if(draw):

                    cv2.circle(fream,(cx,cy),15,(255,0,255),cv2.FILLED)


        return lmList


"""main函数是为了测试上面的类能否正常使用的,在别的文件内导包直接调用上面类的函数即可"""

def main():

    pTime = 0

    cTime = 0

    cap = cv2.VideoCapture(0)


    detector = handDetector()

    while True:

        ret, fream = cap.read()

        cTime = time.time()  # 当前时间

        fps = 1 / (cTime - pTime)

        pTime = cTime

        cv2.putText(fream, "FPS:" + str(int(fps)), (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 2)


        fream = detector.findHands(fream)

        lmList = detector.findPositon(fream)

        if len(lmList) !=0:

            print(lmList[0])


        cv2.imshow("show", fream)

        key = cv2.waitKey(1)

        if key == 27:

            break;

    cap.release()

    cv2.destroyAllWindows()


if __name__ == '__main__':

    main()

 

在别的文件,直接import 这个python文件的文件名,比如我这里命名的是HandTrackingModule.py,就

import HandTrackingModule

然后创建handDetector类,直接调用其中的函数就行了

detector = handDetector()

fream = detector.findHands(fream)

lmList = detector.findPositon(fream)

 

  • 33
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是一个使用Python+OpenCV+MediaPipe实现手势识别系统的项目示例: 1. 安装必要的库,包括OpenCVMediaPipe和NumPy等。 ``` pip install opencv-python mediapipe numpy ``` 2. 导入必要的库和模块: ```python import cv2 import mediapipe as mp import numpy as np ``` 3. 初始化Hand Tracking模块: ```python mp_hands = mp.solutions.hands hands = mp_hands.Hands( static_image_mode=False, max_num_hands=2, min_detection_confidence=0.7, min_tracking_confidence=0.7) ``` 4. 读取摄像头捕获到的图像: ```python cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: continue ``` 5. 对图像中的手部进行跟踪和检测: ```python # 转换图像颜色空间 image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 处理图像 results = hands.process(image) # 将图像颜色空间转换回来 image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) ``` 6. 对检测到的手部进行手势识别,并根据识别结果做出相应的反应: ```python # 检测到手部 if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: # 获取手部关键点坐标 landmarks = np.array([[lmk.x, lmk.y, lmk.z] for lmk in hand_landmarks.landmark]).T # 进行手势识别 gesture = gesture_recognition(landmarks) # 根据手势识别结果做出相应的反应 if gesture == 'Fist': # 做出拳头手势的反应 ... elif gesture == 'Open': # 做出张开手掌的反应 ... else: # 其他手势的反应 ... ``` 7. 释放摄像头和Hand Tracking模块,并关闭窗口: ```python cap.release() hands.close() cv2.destroyAllWindows() ``` 需要注意的是,以上代码只是一个简单的示例,实际的手势识别系统还需要进行模型的训练和优化,以及对不同的手势进行分类和识别

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值