目录
运行效果
挨行解读
(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 # 读取视频流中的一帧
-
ret, frame = cap.read()
: 这一行代码使用cap.read()
方法来读取视频的一帧。cap.read()
返回两个值,第一个是布尔值ret
,表示读取是否成功;第二个是图像帧frame
,是一个表示图像数据的 NumPy 数组。 -
if not ret: break
: 这一行检查ret
的值,如果ret
为False
,表示读取失败(可能是因为视频结束或发生了错误),那么我们就跳出循环,结束程序。
(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)
。我来挨个解释一下:
-
hands
是我们之前通过mp_hands.Hands()
创建的 ,它提供了手部关键点检测的功能。 -
frame_rgb
是一个图像帧的 NumPy 数组,表示图像数据。注意了mediapipe
库要求输入图像为 RGB 格式。 -
hands.process()
是Hands
类中的一个方法,用于处理输入的图像帧。它会对图像进行手部关键点检测,并返回一个包含检测结果的对象。 -
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)
这段代码的主要作用是处理手部关键点检测的结果,将检测到的手部关键点和手指骨骼线绘制在图像上。挨个来看:
-
if results.multi_hand_landmarks:
这个条件语句检查是否检测到了不只一只手。results.multi_hand_landmarks
是一个列表,每个元素代表一个检测到的手部的关键点坐标。 -
for hand_landmarks in results.multi_hand_landmarks:
这个循环遍历每个检测到的手部,其中hand_landmarks
包含了该手部的关键点坐标。 -
mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
: 这一行代码使用drawing_utils
模块的draw_landmarks
函数在图像上绘制手部关键点和手指骨骼线。frame
是要绘制的图像,hand_landmarks
是该手部的关键点坐标,mp_hands.HAND_CONNECTIONS
表示绘制手部关键点之间的连接线。 -
finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]
: 这一行代码获取手部关键点中特定手指的坐标,我们这里选择的是食指。landmark
对象包含了手部关键点的三维坐标(x、y、z)。 -
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)
挨个解释一下
-
current_time = time.time()
: 这一行获取当前时间戳,表示当前时刻的时间。 -
fps = 1 / (current_time - fps_time)
: 这一行计算帧率。帧率是每秒处理的图像帧数。计算公式是总帧数除以总时间,这里使用时间差来估算每秒的帧数。 -
fps_time = current_time
: 更新fps_time
,将其设置为当前时间戳,以便在下一帧时可以继续计算帧率。 -
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
函数来在窗口中显示图像。
-
'Hand grasping'
: 这是窗口的标题,即显示图像窗口的名称。 -
frame
: 这是咱要在窗口中显示的图像。frame
是通过摄像头捕捉到的一帧图像,或者经过处理后的图像。 -
cv2.imshow('Hand grasping', frame)
: 这一行代码将图像frame
显示在窗口中,窗口标题为 'Hand grasping'。imshow
函数用于在窗口中显示图像。
(11)检测按键,按下ESC键退出循环
if cv2.waitKey(1) & 0xFF == 27:break
-
cv2.waitKey(1)
: 这一行代码等待键盘输入,参数1
表示等待 1 毫秒。函数返回按键的 ASCII 值,如果没有按键输入,返回-1
。 -
& 0xFF == 27
: 这个条件语句检查按键的 ASCII 值是否等于 27。ASCII 值为 27 的键是 ESC 键(Escape 键)。 -
if ...: break
: 如果用户按下 ESC 键,即条件为真,那么程序会执行break
语句,跳出循环,从而结束程序的执行。
意思就是按下ESC就退出
(12)最后来释放资源退出程序
-
cap.release()
: 这一行代码释放了通过cv2.VideoCapture(0)
打开的视频捕获对象的资源。 -
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~