使用OpenCV和mediapipe实现手部信息抓取

目录

运行效果

挨行解读

(1)初始化MediaPipe Hand模块

(2)打开摄像头

(3)初始化计时器

(4)开始程序主题部分

(5)读取视频帧

(6)将图像转换为RGB格式(MediaPipe要求输入为RGB格式)

(7)使用MediaPipe Hands模块处理图像

(8)绘制手部关键点及骨骼线

(9)计算FPS

(10)显示处理后的图像

(11)检测按键,按下ESC键退出循环

(12)最后来释放资源退出程序

总体代码

总结


运行效果

由于用两只手我就没法按键盘截图了,就放上一只手的效果吧~~~

挨行解读

(1)初始化MediaPipe Hand模块

mp_hands = mp.solutions.hands

        这一行代码是导入了 mediapipe 库中的 hands 模块,然后通过 mp_hands 变量来引用这个模块。

        说的详细一点的话就是 mediapipe.solutions.hands 模块包含了 Hands 类,该类提供了对手部关键点检测的功能。通过使用这个类,咱们就可以轻松地在图像或视频流中检测手的位置、手指的姿势等信息。

        这样我们就可以在代码中使用 mp_hands 来引用 mediapipe.solutions.hands 模块,使代码更easy!!!

hands = mp_hands.Hands()

        这一行代码创建了一个 mediapipe 库中 Hands 类的实例,这样我们就可以使用 mediapipe 提供的手部关键点检测功能。

        说的详细一点就是 mp_hands.Hands() 的调用初始化了一个手部关键点检测器,可以在输入的图像或视频流中检测手的位置、手指的姿势等信息。这个实例化的对象(hands 变量)具有方法 process(),可以用来对图像进行处理并获得手部关键点的检测结果。

        

mp_drawing = mp.solutions.drawing_utils

        这一行代码导入了 mediapipe 库中的 drawing_utils 模块,并使用 mp_drawing 变量引用了这个模块。

       mediapipe.solutions.drawing_utils 模块包含了一些辅助的函数,可以在图像上绘制关键点、连接线等。这样就可以绘制出手部的效果。

      

(2)打开摄像头

cap = cv2.VideoCapture(0)

        这一行代码使用 OpenCV 中的 cv2.VideoCapture 类创建了一个视频捕捉对象 cap,并打开了系统默认的摄像头(通常是编号为 0 的摄像头)。

   cv2.VideoCapture(0) 传递了参数 0,意思是要打开系统中的第一个摄像头。如果我们有多个摄像头的话,可以通过更改参数来选择不同的摄像头,例如 cv2.VideoCapture(1) 表示打开第二个摄像头。

(3)初始化计时器

fps_time = time.time()

  fps_time = time.time() 我们通过这一行代码初始化一个变量 fps_time,其目的是记录程序开始运行时的时间戳。

(4)开始程序主题部分

while cap.isOpened():

  while cap.isOpened(): 表示在视频捕捉对象 cap 处于打开状态时,循环将一直执行。循环内的代码将不断读取视频帧并进行处理,直到视频捕捉对象被释放或关闭。

       cap.isOpened() 返回一个布尔值,指示视频捕捉对象是否成功打开。如果返回 True,表示对象成功打开,循环会继续执行;如果返回 False,表示对象未成功打开或已经关闭,循环将退出。

        我们这个循环结构是一个非常常见的视频处理模式,这样就可以持续地从摄像头中读取帧并进行处理,直到程序结束。

(5)读取视频帧

    # 读取视频流中的一帧
    ret, frame = cap.read()
    if not ret:
        break    # 读取视频流中的一帧
  1. ret, frame = cap.read(): 这一行代码使用 cap.read() 方法来读取视频的一帧。cap.read() 返回两个值,第一个是布尔值 ret,表示读取是否成功;第二个是图像帧 frame,是一个表示图像数据的 NumPy 数组。

  2. if not ret: break: 这一行检查 ret 的值,如果 retFalse,表示读取失败(可能是因为视频结束或发生了错误),那么我们就跳出循环,结束程序。

(6)将图像转换为RGB格式(MediaPipe要求输入为RGB格式)

frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

(7)使用MediaPipe Hands模块处理图像

results = hands.process(frame_rgb)

        这一行代码使用了 mediapipe 库中 Hands 类的 process 方法来处理图像帧,即 hands.process(frame_rgb)。我来挨个解释一下:

  1. hands 是我们之前通过 mp_hands.Hands() 创建的 ,它提供了手部关键点检测的功能。

  2. frame_rgb 是一个图像帧的 NumPy 数组,表示图像数据。注意了mediapipe 库要求输入图像为 RGB 格式。

  3. hands.process()Hands 类中的一个方法,用于处理输入的图像帧。它会对图像进行手部关键点检测,并返回一个包含检测结果的对象。

  4. results 是接收检测结果的变量。通过调用 hands.process(frame_rgb),检测结果将被存储在 results 中。这个结果包括了检测到的手的位置、手指关键点的坐标等信息。

        因此,这一行代码的目的是使用 mediapipe 库提供的手部关键点检测功能,处理输入的图像帧,并将检测结果存储在 results 变量中,然后我们后续的代码就可以通过results来实现可视化。

(8)绘制手部关键点及骨骼线

    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            # 获取手指关键点
            finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]

            # 绘制手指骨骼线
            for i in range(4):
                start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))
                end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))
                cv2.line(frame, start_point, end_point, (255, 0, 255), 2)

        这段代码的主要作用是处理手部关键点检测的结果,将检测到的手部关键点和手指骨骼线绘制在图像上。挨个来看:

  1. if results.multi_hand_landmarks:这个条件语句检查是否检测到了不只一只手。results.multi_hand_landmarks 是一个列表,每个元素代表一个检测到的手部的关键点坐标。

  2. for hand_landmarks in results.multi_hand_landmarks: 这个循环遍历每个检测到的手部,其中 hand_landmarks 包含了该手部的关键点坐标。

  3. mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS): 这一行代码使用 drawing_utils 模块的 draw_landmarks 函数在图像上绘制手部关键点和手指骨骼线。frame 是要绘制的图像,hand_landmarks 是该手部的关键点坐标,mp_hands.HAND_CONNECTIONS 表示绘制手部关键点之间的连接线。

  4. finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]: 这一行代码获取手部关键点中特定手指的坐标,我们这里选择的是食指。landmark 对象包含了手部关键点的三维坐标(x、y、z)。

  5. for i in range(4): ...: 这个循环遍历食指的关键点,然后再绘制手指骨骼线。在每次迭代中,获取相邻两个关键点的坐标,然后使用 cv2.line() 绘制一条连接这两个点的线段。

        这样,我们就实现了可视化手部的姿势。

(9)计算FPS

 # 计算帧率
    current_time = time.time()
    fps = 1 / (current_time - fps_time)
    fps_time = current_time

    # 显示帧率
    cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

        挨个解释一下

  1. current_time = time.time(): 这一行获取当前时间戳,表示当前时刻的时间。

  2. fps = 1 / (current_time - fps_time): 这一行计算帧率。帧率是每秒处理的图像帧数。计算公式是总帧数除以总时间,这里使用时间差来估算每秒的帧数。

  3. fps_time = current_time: 更新 fps_time,将其设置为当前时间戳,以便在下一帧时可以继续计算帧率。

  4. cv2.putText(...): 这一行使用 OpenCV 的 putText 函数在图像上绘制帧率信息。具体参数为:

    • frame: 要绘制文本的图像。
    • f'FPS: {int(fps)}': 要显示的文本,包括字符串 "FPS: " 和计算得到的帧率值。
    • (10, 30): 文本的位置,即左上角的坐标。
    • cv2.FONT_HERSHEY_SIMPLEX: 字体类型。
    • 1: 字体大小。
    • (0, 255, 0): 文本颜色,这里是绿色,RGB嘛g就是绿色哪个单词的首字母。
    • 2: 文本的线宽。

        这样就可以实时显示FPS了,就很爽~~~^_^

(10)显示处理后的图像

    cv2.imshow('Hand grasping', frame)

使用 OpenCV 的 imshow 函数来在窗口中显示图像。

  1. 'Hand grasping': 这是窗口的标题,即显示图像窗口的名称。

  2. frame: 这是咱要在窗口中显示的图像。frame 是通过摄像头捕捉到的一帧图像,或者经过处理后的图像。

  3. cv2.imshow('Hand grasping', frame): 这一行代码将图像 frame 显示在窗口中,窗口标题为 'Hand grasping'。imshow 函数用于在窗口中显示图像。

(11)检测按键,按下ESC键退出循环

    if cv2.waitKey(1) & 0xFF == 27:
        break

    

  1. cv2.waitKey(1): 这一行代码等待键盘输入,参数 1 表示等待 1 毫秒。函数返回按键的 ASCII 值,如果没有按键输入,返回 -1

  2. & 0xFF == 27: 这个条件语句检查按键的 ASCII 值是否等于 27。ASCII 值为 27 的键是 ESC 键(Escape 键)。

  3. if ...: break: 如果用户按下 ESC 键,即条件为真,那么程序会执行 break 语句,跳出循环,从而结束程序的执行。

        意思就是按下ESC就退出

(12)最后来释放资源退出程序

  1. cap.release(): 这一行代码释放了通过 cv2.VideoCapture(0) 打开的视频捕获对象的资源。

  2. cv2.destroyAllWindows(): 这一行代码关闭所有通过 OpenCV 创建的窗口。如果关闭时出现问题,可能会在程序结束后窗口继续存在。那么我们通过调用 destroyAllWindows()来确保在程序退出时所有图形窗口都肯定会被关闭。


总体代码

import cv2
import mediapipe as mp
import time

# 初始化MediaPipe Hand模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils

# 打开摄像头
cap = cv2.VideoCapture(0)

# 初始化计时器
fps_time = time.time()

while cap.isOpened():
    # 读取视频流中的一帧
    ret, frame = cap.read()
    if not ret:
        break

    # 将图像转换为RGB格式(MediaPipe要求输入为RGB格式)
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 使用MediaPipe Hands模块处理图像
    results = hands.process(frame_rgb)

    # 绘制手部关键点及骨骼线
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            # 获取手指关键点
            finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]

            # 绘制手指骨骼线
            for i in range(4):
                start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))
                end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))
                cv2.line(frame, start_point, end_point, (255, 0, 255), 2)

    # 计算帧率
    current_time = time.time()
    fps = 1 / (current_time - fps_time)
    fps_time = current_time

    # 显示帧率
    cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    # 显示处理后的图像
    cv2.imshow('Hand grasping', frame)

    # 检测按键,按下ESC键退出循环
    if cv2.waitKey(1) & 0xFF == 27:
        break

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

总结

以后再总结~!~~~~ヾ( ̄▽ ̄)Bye~Bye~

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WenJGo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值