MediaPipe上手案例,手部骨架识别,用视频替换代码摄像头采集

Posted 梦想橡皮擦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MediaPipe上手案例,手部骨架识别,用视频替换代码摄像头采集相关的知识,希望对你有一定的参考价值。

文章目录


在做 MediaPipe 项目的时候,经常需要唤醒摄像头进行视频采集,学习的时候可以使用视频替换摄像头采集动作,这就是本篇博客的的由来。

MediaPipe 入门案例

为了便于学习,我们先直接展示入门案例,然后查看从代码角度进行讲解。

import cv2
import mediapipe as mp

# 创建VideoCapture对象,读取视频
cap = cv2.VideoCapture('./demo.mp4')

# 初始化mediapipe
mp_hands = mp.solutions.hands.Hands()

while cap.isOpened():
    # 读取视频帧
    success, image = cap.read()
    if not success:
        print("读取失败")
        continue

    # 将图像转换为RGB格式
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

    # 处理图像
    results = mp_hands.process(image)

    # 将图像转换回BGR格式
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    # 显示结果
    cv2.imshow('MediaPipe Hands', image)
    if cv2.waitKey(5) & 0xFF == 27:
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

为了让代码跑起来,需要提前安装 opencv-python 库和 mediapipe 库,直接使用 pip 命令安装即可。

视频我们使用了 B 站 @蜜加原创手指舞 的视频,只做学习演示 Demo。

上述代码运行结果如下所示。


接下来我们逐行解释一下代码。

读取视频文件

# 创建VideoCapture对象,读取视频
cap = cv2.VideoCapture('./demo.mp4')

上述代码初始化了一个名为 capVideoCapture 对象,用于从当前目录中名为 'demo.mp4' 的视频文件中读取帧。

cv2.VideoCapture() 函数接受一个参数,即视频文件的路径。

VideoCapture 对象可以用于从视频文件中读取帧,也可以用于从摄像头中读取帧。如果要从摄像头中读取帧,则可以将参数设置为摄像头的索引,例如 cap = cv2.VideoCapture(0),其中 0 表示默认摄像头的索引。

如果只是单纯查看视频,可以使用下述代码:

import cv2

# 初始化对象
cap = cv2.VideoCapture('./demo.mp4')

# 检查视频是否打开
if not cap.isOpened():
    print("视频文件打开失败")

# 读取视频
while cap.isOpened():
    # 逐帧读取
    ret, frame = cap.read()

    if ret:
        # 展示读取结果
        cv2.imshow('帧', frame)

        # 按 Q 退出
        if cv2.waitKey(25) & 0xFF == ord('q'):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()

初始化 Hands 对象

mp_hands = mp.solutions.hands.Hands()

mp.solutions.hands.Hands() 函数创建 Hands 类的一个新实例,该类是 MediaPipe Hands 解决方案的一部分。

该类提供了检测图像或视频帧中手部的方法,并返回有关检测到的手部的信息,例如它们的关键点和左右手性。

接下来可以在循环中读取视频的每一帧,然后使用 mp_hands.process(imgRGB) 获取【手位置】。

import cv2
import mediapipe as mp

cap = cv2.VideoCapture('./demo.mp4')
# 定义并引用mediapipe中的hands模块
mp_hands = mp.solutions.hands.Hands()

while True:
    success, img = cap.read()
    imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # cv2图像初始化
    results = mp_hands.process(imgRGB)
    print(results.multi_hand_landmarks)

    cv2.imshow("HandsImage", img)  # CV2窗体
    cv2.waitKey(1)  # 关闭窗体

代码运行得到如下结果。

landmark 
  x: 0.18026168644428253
  y: 0.4238351583480835
  z: -0.0214223675429821

landmark 
  x: 0.45827752351760864
  y: 0.39915671944618225
  z: 0.011419275775551796

mp_hands.process(imgRGB) 是 MediaPipe Hands 解决方案的一部分,用于在 RGB 图像中检测手部。
它接受一个 RGB 图像作为输入,并返回一个 SolutionOutputs 对象,其中包含有关检测到的手部的信息。

results.multi_hand_landmarks 是 SolutionOutputs 对象的一个属性,它包含一个列表,其中每个元素都是一个 HandLandmark 对象,表示检测到的一个手部的关键点。如果没有检测到手部,则 results.multi_hand_landmarks 为 None。

每个 HandLandmark 对象包含一个 landmark 属性,它是一个列表,其中每个元素都是一个 NormalizedLandmark 对象,表示手部的一个关键点。
NormalizedLandmark 对象包含 xyz 属性,表示关键点在图像坐标系中的归一化坐标。
xy 属性的值在 [0, 1] 范围内,表示关键点在图像中的位置

z 属性的值表示关键点的深度,但在 MediaPipe Hands 解决方案中未使用。

在关键位置绘制图形

前文已经读取到手部识别的关键节点,接下来在读取到的位置处绘制图形。

    if results.multi_hand_landmarks:
        for handLms in results.multi_hand_landmarks:
            for index, x_y in enumerate(handLms.landmark):

                h, w, c = img.shape
                # 获取节点关键坐标
                cx, cy = int(x_y.x * w), int(x_y.y * h)
                # 绘制一个圆形
                cv2.circle(img, (cx, cy), 7, (4, 255, 255), cv2.FILLED)


接下来我们实现连接手指骨架,在上述代码中输入如下代码。

mp_draw = mp.solutions.drawing_utils

mp.solutions 提供了使用 MediaPipe 解决方案在图像或视频帧上绘制关键点和连接的实用函数。

该模块包含几个函数,可用于在图像或视频帧上绘制关键点和连接,包括 draw_landmarks()draw_connections()draw_landmarks_on_image()。这些函数的输入参数包括要绘制的图像或帧,以及要绘制的关键点或连接。

下面演示如何使用 mp.solutions.drawing_utils 在图像上绘制关键点和连接的示例代码:

# 绘制手部特征点:
mp_draw.draw_landmarks(img, handLms, mp.solutions.hands.HAND_CONNECTIONS)

运行代码得到如下效果。


在上述代码中用到了 draw_landmarks() 函数。

该函数用于在图像中绘制手部关键点和连接。它接受三个参数:

  • img:要在其上绘制手部关键点和连接的图像。
  • handLms:一个 HandLandmark 对象,表示要绘制的手部的关键点。可以从 SolutionOutputs 对象的 multi_hand_landmarks 属性中获取 HandLandmark 对象。
  • mp.solutions.hands.HAND_CONNECTIONS:一个常量,表示要绘制的手部关键点之间的连接。

mp_draw.draw_landmarks() 函数原型说明

该函数还可以接受其他参数,用来控制绘制的手部关键点和连接的外观,其原型如下所示:

mp_draw.draw_landmarks(
    image: np.ndarray,
    landmark_list: Union[mp_hands.HandLandmarkList, mp_pose.PoseLandmarkList],
    connections: Optional[mp_drawing.DrawingSpec] = None,
    landmark_drawing_spec: Optional[mp_drawing.DrawingSpec] = None,
    connection_drawing_spec: Optional[mp_drawing.DrawingSpec] = None
) -> None

  • landmark_list 参数是一个 HandLandmarkList 或 PoseLandmarkList 对象,表示要绘制的手部或姿势的关键点。
  • connections 参数是一个 DrawingSpec 对象,表示要绘制的关键点之间的连接。
  • landmark_drawing_specconnection_drawing_spec 参数分别是 DrawingSpec 对象,表示要用于绘制关键点和连接的颜色、线宽和半径。

以下是 DrawingSpec 类的构造函数:

mp_drawing.DrawingSpec(
    color: Tuple[int, int, int],
    thickness: int = 1,
    circle_radius: Optional[int] = None
)
  • color 参数是一个 RGB 元组,表示要用于绘制关键点和连接的颜色。
  • thickness 参数是一个整数,表示要用于绘制连接的线宽。
  • circle_radius 参数是一个整数,表示要用于绘制关键点的半径。

完善一下代码,添加关键点颜色。

# 连接线样式
lineStyle = mp_draw.DrawingSpec()
lineStyle.color = (0, 0, 255)
lineStyle.thickness = 1
# 绘制手部特征点:
mp_draw.draw_landmarks(img, handLms, mp.solutions.hands.HAND_CONNECTIONS, landmark_drawing_spec=lineStyle)

基于python-mediapipe手部识别

前言

今天学习了一个非常神奇的手部识别模块mediapipe,只是其中一个功能还有很多功能以后有时间再去学习

效果如图

在这里做一下记录

学习记录

首先呢我们先安装必要的模块

mediapipe
opencv-python  =>cv2  
我们用的工具时pycharm  直接在那个模块下点加号搜索其模块安装就行

好了 安装好了之后就开始用了

首先是

cap= cv2.VideoCapture()
参数是0的话表示打开笔记本的内置摄像头,参数是视频文件路径则打开视频 返回一个对象

有了对象之后就可以按帧来读了

success,img= cap.read() 读取图像帧 有两个返回值,第一个是是否读取成功,第二个是每一帧的图像

再就是cv2.waitKey()用法

参数是1,表示延时1ms切换到下一帧图像,对于视频而言

参数为0,如cv2.waitKey(0)只显示当前帧图像,相当于视频暂停

今天的主角是这个

生成手部对象,注意的是,在后面处理的是RGB格式图像 所以使用 hands.process()处理的图像必须是RGB格式
myHands= mediapipe.soultions.hands
hands= myHands.Hands()

当然也离不开画图模块

	#对产生的result.multi_hand_landmarks标志物进行连接
    mpDraw = mediapipe.soultions.drawing_utils
    img_R=  cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    result = hands.process(img_R)
    #result.multi_hand_landmarks是检测到所有手的列表,对该列表进行访问我们可以得到每只手对应标志位的信息
     for handLms in  result.multi_hand_landmarks:
     	 mpDraw.draw_landmarks(img, handLms, myHands.HAND_CONNECTIONS)

也可以用cv2.putText()方法进行标记

cv2.putText(img, str(int(id)), (cx , cy ), cv2.FONT_HERSHEY_PLAIN,1, (0, 0, 255), 2)
贴大佬的解释
参数:
image:它是要在其上绘制文本的图像。
text:要绘制的文本字符串。
org:它是图像中文本字符串左下角的坐标。坐标表示为两个值的元组,即(X坐标值,Y坐标值)。
font:它表示字体类型。一些字体类型是FONT_HERSHEY_SIMPLEX,FONT_HERSHEY_PLAIN,等
fontScale:字体比例因子乘以font-specific基本大小。
color:它是要绘制的文本字符串的颜色。对于BGR,我们通过一个元组。例如:(25500)为蓝色。
thickness:它是线的粗细像素。
lineType:这是一个可选参数,它给出了要使用的行的类型。
bottomLeftOrigin:这是一个可选参数。如果为true,则图像数据原点位于左下角。否则,它位于左上角。

相关代码为

import  cv2

import  mediapipe as mp
import time
#视频操作函数
#也可以导入视频
cap = cv2.VideoCapture(0)
#手部跟踪  处理的事RGB格式 所以使用 hands.process()处理的图像必须是RGB格式
myHands= mp.solutions.hands
hands= myHands.Hands()
mpDraw = mp.solutions.drawing_utils
pTime = 0
cTime = 0
while 1:
    #读取摄像头每一帧并显示
    success,img= cap.read()
    cv2.imshow("image",img)
    #必须是RGB格式 而得到的图像默认是BGR格式所以要转
    img_R=  cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    result = hands.process(img_R)
    #检测所有手的列表,对列表进行访问可以获得 手的位置信息
    if(result.multi_hand_landmarks):
        for handLms in  result.multi_hand_landmarks:
            #每一个标志位都有一个id 获取并将其显示
            for id,lm in enumerate(handLms.landmark):
                h, w, c = img.shape
                #获取界面中的坐标 ,这里经过测试是获取的小数需要*界面获取真正坐标
                cx, cy = int(lm.x*w), int(lm.y*h)
                cv2.putText(img, str(int(id)), (cx , cy ), cv2.FONT_HERSHEY_PLAIN,
                            1, (0, 0, 255), 2)
    cv2.imshow("Image", img)
    cv2.waitKey(1)
换成
mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS)
就成了一开始图片的结果

当然也可以加

fps

操作为
 cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime

    cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,
                (255, 0, 255), 3)

所以最后效果为


贴一下写的代码

import  cv2

import  mediapipe as mp
import time
#视频操作函数
#也可以导入视频
cap = cv2.VideoCapture(0)
#手部跟踪  处理的事RGB格式 所以使用 hands.process()处理的图像必须是RGB格式
myHands= mp.solutions.hands
hands= myHands.Hands()
mpDraw = mp.solutions.drawing_utils
pTime = 0
cTime = 0
while 1:
    #读取摄像头每一帧并显示
    success,img= cap.read()
    cv2.imshow("image",img)
    img_R=  cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    result = hands.process(img_R)
    #检测所有手的列表,对列表进行访问可以获得 手的位置信息
    if(result.multi_hand_landmarks):
        for handLms in  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)
                #cv2.putText(img, str(int(id)), (cx , cy ), cv2.FONT_HERSHEY_PLAIN,
                #            1, (0, 0, 255), 2)
                #mpDraw.draw_landmarks(img, handLms, myHands.HAND_CONNECTIONS)
                #然后进行画图
            mpDraw.draw_landmarks(img, handLms, myHands.HAND_CONNECTIONS)

    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime

    cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,
                (255, 0, 255), 3)

    cv2.imshow("Image", img)


    cv2.waitKey(1)






最后的总结是:太神奇了,学到了模块的一小部分,就可以实现这个,还有更多东西需要学习.

参考链接:
https://www.computervision.zone

以上是关于MediaPipe上手案例,手部骨架识别,用视频替换代码摄像头采集的主要内容,如果未能解决你的问题,请参考以下文章

[mediapipe][转载]手部识别

Mediapipe+VSCode+Anaconda 实时检测手部关键点并保存视频

机器学习实验四基于Logistic Regression二分类算法实现手部姿态识别

在 Python 3.7.9 中使用 opencv 和 mediapipe 制作手部跟踪​​模块时出错

圣诞纯情手势告白(Mediapipe基本使用&手势识别详解)

圣诞纯情手势告白(Mediapipe基本使用&手势识别详解)