【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)

 

### MediaPipeYOLO的功能及应用场景对比 #### 1. **核心功能** MediaPipe 是一个跨平台框架,专注于提供高效的计算机视觉和机器学习解决方案[^2]。其主要功能包括但不限于人体姿态估计、面部识别、手势识别以及3D目标检测跟踪等复杂任务。而 YOLO(You Only Look Once)是一种实时目标检测算法,主要用于二维空间中的对象定位和分类[^1]。 尽管两者都属于计算机视觉领域,但它们的核心侧重点不同: - **MediaPipe** 更倾向于多模态感知能力,例如通过深度信息实现3D重建或姿态追踪等功能。 - **YOLO** 主要用于平面图像上的快速目标检测,在速度和准确性之间取得了良好的平衡。 --- #### 2. **性能表现** 从性能角度来看: - **YOLO** 的优势在于极高的推理速度,能够在保持较高精度的同时实现实时处理。这得益于其单次前向传播的设计架构。 - 对于 **MediaPipe** 而言,虽然也具备较高的运行效率,但由于某些模块依赖复杂的计算流程(如深度图生成),因此整体延迟可能会稍高于纯轻量级模型的应用场景。 具体来说,如果仅考虑简单的边界框绘制任务,则 YOLO 可能更胜一筹;但如果需要结合其他高级特性(比如手部关键点提取或者 AR 效果叠加),那么 MediaPipe 将成为更好的选择。 --- #### 3. **适用场景** 以下是两种技术各自擅长的应用范围: | 特性/场景 | MediaPipe | YOLO | |-------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------| | **静态图片分析** | 支持,但对于单一帧内的细节挖掘不如专门优化过的传统 CV 方法强大 | 极佳效果,尤其适合批量处理大量独立样本 | | **动态视频流处理** | 出色支持,能够无缝衔接前后帧间的信息传递 | 同样优秀,不过缺乏内置的时间序列关联机制 | | **增强现实 (AR)** | 原生兼容性强 | 需额外开发适配层 | | **多人交互环境** | 天然适应群组模式下的动作捕捉 | 默认版本不直接支持此类情况 | 值得注意的是,在实际项目部署过程中还需综合考量硬件资源限制等因素的影响。 --- #### 4. **扩展性和灵活性** 当面对高度定制化的需求时: - 使用 **OpenCV 结合 MediaPipe** 方便调用现有成熟组件完成基础搭建工作后再做进一步调整[^4]; - 如果偏好完全自主掌控整个pipeline设计权衡则更适合采用基于 TensorFlow 或 PyTorch 实现的新版 YOLOv5/v7 系列产品。 此外需要注意一点就是关于手势控制方面的差异——MediaPipe 不仅有对应的手势跟踪还自带了预训练好的分类器可以直接拿来即插即用;然而对于后者而言如果没有特别指定的话默认是没有这个选项可用的除非自行训练相应网络结构出来才行[^4]. --- ```python import cv2 from mediapipe import solutions as mp_solutions # 初始化 MediaPipe 手部检测器 hands = mp_solutions.hands.Hands() cap = cv2.VideoCapture(0) while cap.isOpened(): success, image = cap.read() if not success: break results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) if results.multi_hand_landmarks is None: continue for handLms in results.multi_hand_landmarks: # 绘制标记点 pass cv2.destroyAllWindows() ``` 以上代码片段展示了如何利用 MediaPipe 来获取并显示手上各个关节的位置坐标值。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

owooooow

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

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

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

打赏作者

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

抵扣说明:

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

余额充值