目录
[1. 依赖库导入](#1. 依赖库导入)
[2. MediaPipe手部检测器初始化](#2. MediaPipe手部检测器初始化)
[3. 视频采集与核心检测循环](#3. 视频采集与核心检测循环)
[4. 关键点可视化与坐标输出](#4. 关键点可视化与坐标输出)
[5. 窗口显示与资源释放](#5. 窗口显示与资源释放)
在计算机视觉领域,手部关键点检测是手势控制、人机交互、AR/VR等应用的核心基础。而Google开源的MediaPipe框架,凭借其高精度、低延迟、轻量化的优势,无需我们手动训练模型,就能快速实现手部关键点的实时检测与追踪,非常适合新手入门学习。
本文将结合一段完整的实战代码,从零拆解MediaPipe + OpenCV实现手部检测的全流程,详细讲解每个模块的作用、关键参数的含义以及新手常踩的坑,帮助大家快速上手MediaPipe的手部检测功能,同时也记录下自己的学习心得,供同为新手的小伙伴参考。
这里是一片详细的mediapipe库介绍的博客网址:https://www.cnblogs.com/yxysuanfa/p/19154261
一、核心功能概述
本次实战代码基于MediaPipe和OpenCV,实现了以下核心功能:
-
调用电脑默认摄像头,采集实时视频流;
-
实时检测画面中最多2只手的21个关键点,输出每个关键点的三维坐标;
-
在视频画面上标注每个关键点的序号,绘制关键点及手部骨骼连接线条,直观呈现检测结果;
-
支持ESC键退出实时检测,自动释放摄像头资源,避免占用。
二、完整实战代码
python
import cv2
import mediapipe as mp
'''
mp.solutions.drawing_utils是一个绘图模块,将识别到的手部关键点信息绘制到cv2图像中,mp.solutions.drawing_style定义了绘制的风格。
mp.solutions.hands是mediapipe中的手部识别模块,可以通过它调用手部识别的api,然后通过调用mp_hands.Hands初始化手部识别类。
mp_hands.Hands中的参数:
1) static_image_mode=True适用于静态图片的手势识别,False适用于视频等动态识别,比较明显的区别是,若识别的手的数量超过了最大值,
True时识别的手会在多个手之间不停闪烁,而False时,超出的手不会识别,系统会自动跟踪之前已经识别过的手。默认值为False;
2) max_num_hands用于指定识别手的最大数量。默认值为2;
3) min_detection_confidence 表示最小检测信度,取值为[0.0,1.0]这个值越小越容易识别出手,用时越短,但是识别的准确度就越差。越大识别的越精准,
但是响应的时间也会增加。默认值为0.5;
4) min_tracking_confidence 表示最小的追踪可信度,越大手部追踪的越准确,相应的响应时间也就越长。默认值为0.5。
'''
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=2,
min_detection_confidence=0.75,
min_tracking_confidence=0.75)
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
h,w=frame.shape[:2]
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 因为摄像头是镜像的,所以将摄像头水平翻转
# 不是镜像的可以不翻转
frame = cv2.flip(frame, 1)
results = hands.process(frame)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 计算关键点的距离,用于判断手指是否伸直
for i in range(len(hand_landmarks.landmark)):
x = hand_landmarks.landmark[i].x
y = hand_landmarks.landmark[i].y
z = hand_landmarks.landmark[i].z
print(x,y,z)
cv2.putText(frame, str(i), (int(x*w),int(y*h)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0),2)
# 关键点可视化
mp_drawing.draw_landmarks(frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
运行效果展示:
三、代码逐模块详细解析
下面我们逐行、逐模块拆解代码,搞懂每个部分的作用,避免"复制粘贴跑通代码就完事",真正理解背后的逻辑。
1. 依赖库导入
python
import cv2
import mediapipe as mp
这两行代码导入了实现功能所需的两个核心库,缺一不可:
-
cv2(OpenCV):开源计算机视觉库,主要负责视频采集(调用摄像头)、图像格式转换、画面绘制(文字、线条)、窗口显示和资源释放等操作,是计算机视觉实战的基础工具。
-
mediapipe:谷歌开源的跨平台机器学习框架,内置了成熟的手部检测模型,封装了完整的API,我们无需手动训练模型,直接调用即可实现手部关键点检测,极大降低了开发难度。
2. MediaPipe手部检测器初始化
python
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=2,
min_detection_confidence=0.75,
min_tracking_confidence=0.75)
这部分是MediaPipe手部检测的核心配置,我们逐一解析:
-
mp_drawing = mp.solutions.drawing_utils:导入MediaPipe的绘图工具,用于将检测到的手部关键点和骨骼连接线条绘制到视频画面上,让检测结果可视化。 -
mp_hands = mp.solutions.hands:导入MediaPipe的手部检测核心模块,该模块内部封装了手掌检测和关键点回归的两阶段架构,能高效定位手部区域并预测关键点坐标。 -
hands = mp_hands.Hands(...):初始化手部识别类,通过参数配置检测效果,四个核心参数的详细说明如下(新手重点关注):-
static_image_mode:检测模式,False为动态视频模式(默认),True为静态图片模式。动态模式下,系统会自动跟踪已检测到的手,避免频繁重新检测导致的画面闪烁;静态模式则每帧都重新检测,适合单张图片处理。 -
max_num_hands:最多可检测的手的数量,默认值为2,可根据需求调整(如仅需检测单只手,可改为1)。 -
min_detection_confidence:最小检测置信度,取值范围[0.0, 1.0]。值越小,越容易识别到手,但准确率越低、响应越快;值越大,识别越精准,但响应越慢。新手建议先设为0.5快速看到效果,后续调至0.75平衡精度和速度。 -
min_tracking_confidence:最小追踪置信度,取值范围[0.0, 1.0]。值越大,手部追踪越稳定,但响应越慢;若检测时出现手部"丢失"的情况,可适当调低该值。
-
3. 视频采集与核心检测循环
python
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
h,w=frame.shape[:2]
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 因为摄像头是镜像的,所以将摄像头水平翻转
# 不是镜像的可以不翻转
frame = cv2.flip(frame, 1)
results = hands.process(frame)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
这部分实现了摄像头调用和实时检测的核心循环,是新手最容易踩坑的地方,重点解析:
-
cap = cv2.VideoCapture(0):调用电脑默认摄像头(编号为0),若外接摄像头,可将0改为1或2(根据摄像头编号调整)。 -
while True::开启无限循环,持续读取摄像头帧并进行检测,直到按下ESC键退出。 -
ret, frame = cap.read():读取摄像头的一帧图像,ret表示读取成功(True)或失败(False),frame表示读取到的图像帧。建议新手补充异常处理:若ret为False(帧读取失败),则break退出循环,避免报错。 -
h,w=frame.shape[:2]:获取图像帧的高度(h)和宽度(w),后续用于将关键点的归一化坐标转换为像素坐标,以便在画面上标注。 -
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB):新手必避坑! OpenCV读取的图像默认是BGR颜色空间,而MediaPipe要求输入图像为RGB颜色空间,若不进行转换,检测结果会完全错误,这是最常见的入门错误。 -
frame = cv2.flip(frame, 1):将图像水平翻转,解决摄像头镜像问题(默认摄像头采集的画面是镜像的,翻转后操作更直观,如你的左手在画面中显示为左手),非必需,可根据需求删除。 -
results = hands.process(frame):将预处理后的RGB图像传入手部检测器,执行检测操作,返回的results对象包含了所有检测到的手部信息(关键点坐标等)。 -
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR):将图像颜色空间从RGB转换回BGR,因为OpenCV显示图像时需要BGR格式,否则画面颜色会失真(如画面偏蓝)。
4. 关键点可视化与坐标输出
python
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 遍历每个关键点(共21个),输出坐标+标注序号
for i in range(len(hand_landmarks.landmark)):
x = hand_landmarks.landmark[i].x
y = hand_landmarks.landmark[i].y
z = hand_landmarks.landmark[i].z
print(x,y,z)
cv2.putText(frame, str(i), (int(x*w),int(y*h)),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0),2)
# 绘制关键点+手部骨骼连接
mp_drawing.draw_landmarks(frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS)
这部分实现了关键点的坐标输出和可视化,让我们能直观看到检测结果,解析如下:
-
if results.multi_hand_landmarks::判断是否检测到手部关键点。results.multi_hand_landmarks是一个列表,存储了所有检测到的手的关键点信息,若未检测到手,则该列表为空,不执行后续绘制操作。 -
for hand_landmarks in results.multi_hand_landmarks::遍历每只检测到的手,hand_landmarks存储了单只手的21个关键点信息。 -
内层循环(遍历21个关键点):
-
x = hand_landmarks.landmark[i].x、y = hand_landmarks.landmark[i].y、z = hand_landmarks.landmark[i].z:获取第i个关键点的三维坐标,其中x、y是归一化坐标(取值0~1,相对于画面宽高),z是深度坐标(以手腕为原点,值越小,关键点越靠近摄像头)。 -
print(x,y,z):在控制台打印关键点的三维坐标,可用于后续的手势识别(如判断手指弯曲、手部前后移动等)。 -
cv2.putText(...):在图像帧上标注关键点的序号(0~20),颜色为绿色(0, 255, 0),字体为cv2.FONT_HERSHEY_SIMPLEX,字号1,线条粗细2。这里需要注意:x、y是归一化坐标,需乘以画面宽(w)、高(h),转换为像素坐标,才能准确标注在对应位置。
-
-
mp_drawing.draw_landmarks(...):绘制手部关键点和骨骼连接线条。其中mp_hands.HAND_CONNECTIONS是MediaPipe内置的手部骨骼连接规则,无需我们手动定义,能自动连接各个关键点,形成完整的手部骨架。
补充:21个关键点的编号规则(新手必备):0是手腕,1~4是拇指(4为指尖),5~8是食指(8为指尖),9~12是中指,13~16是无名指,17~20是小指,掌握编号规则,后续可轻松实现手势判断(如比耶、点赞等)。
5. 窗口显示与资源释放
python
cv2.imshow('MediaPipe Hands', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
这部分是程序的收尾操作,负责显示检测窗口和释放资源,解析如下:
-
cv2.imshow('MediaPipe Hands', frame):创建名为"MediaPipe Hands"的窗口,显示处理后的图像帧(包含关键点标注和骨骼线条),可将窗口名改为更直观的名称(如"实时手部关键点检测")。 -
if cv2.waitKey(1) & 0xFF == 27: break:设置退出条件,cv2.waitKey(1)表示每帧停留1毫秒,监听键盘输入,若按下ESC键(ASCII码为27),则退出循环,结束检测。 -
cap.release():释放摄像头资源,避免摄像头被程序占用(若不释放,后续可能无法正常调用摄像头)。 -
cv2.destroyAllWindows():关闭所有OpenCV创建的窗口,避免窗口残留。
四、代码运行前置条件
新手运行代码前,需先完成以下准备工作,避免报错:
-
安装依赖库 :打开终端(或命令提示符),执行以下命令,安装mediapipe和opencv-python两个库,推荐使用Python 3.8+版本,兼容性更好:
pip install mediapipe opencv-python -
检查摄像头:确保电脑有可用摄像头(内置或外接),且无其他程序占用(如微信视频、Zoom等),否则会出现无法读取帧的错误。
-
解决报错 :若运行时出现MediaPipe相关错误(如MediaPipeError),可尝试更新mediapipe版本:
pip install --upgrade mediapipe若在ARM架构设备(如树莓派)上运行,需安装对应版本的MediaPipe wheel包。
五、功能拓展思路(进阶学习),后续博客内容
掌握基础的手部检测后,可基于这段代码实现更有趣的功能,拓展学习方向如下,新手可逐步尝试:
-
手势计数(数手指):通过计算关键点之间的距离(如指尖与指根的距离),判断手指是否伸直,进而实现1~5的手指计数,可用于简单的数字交互。
-
简单手势控制:定义特定手势(如比耶、点赞、握拳),通过判断关键点的位置关系,实现手势触发操作(如比耶截图、握拳退出程序、点赞调节音量)。
-
彩虹骨骼可视化:优化绘制效果,为每根手指设置不同颜色的骨骼线条,提升视觉辨识度,尤其适合复杂手势的展示。
-
手部深度交互:利用z坐标(深度信息),实现"抬手靠近摄像头触发操作""挥手切换画面"等基于深度的交互功能。
-
多人手部检测:调整max_num_hands参数,实现多人手部同时检测,可用于双人手势交互、手势教学等场景。
六、学习总结
通过这段实战代码,我们掌握了MediaPipe手部检测的核心用法,总结几个关键要点,帮助大家巩固学习:
-
MediaPipe Hands类的static_image_mode参数,需根据场景(静态图片/动态视频)调整,动态场景设为False可避免检测闪烁,提升追踪稳定性。
-
OpenCV与MediaPipe配合的核心是颜色空间转换(BGR↔RGB),这是新手最容易踩的坑,不可遗漏。
-
手部关键点坐标为归一化值(0~1),需乘以画面宽/高,转换为像素坐标,才能在画面上准确标注和绘制。
-
检测精度与速度是权衡关系,新手建议先以"能运行、看到效果"为目标,再逐步调整置信度参数,优化检测效果。
-
MediaPipe的优势的是轻量化、高精度、无需手动训练,非常适合新手入门计算机视觉,后续可尝试其人脸检测、姿态识别等其他功能,拓展技术边界。
本文是自己学习MediaPipe手部检测的实战总结,代码简单易懂,注释详细,适合零基础新手入门。如果运行过程中遇到其他问题,欢迎在评论区交流,一起进步~