【计算机视觉】手势识别
手势识别是计算机视觉领域中的重要方向,通过对摄像机采集的手部相关的图像序列进行分析处理,进而识别其中的手势,手势被识别后用户就可以通过手势来控制设备或者与设备交互。完整的手势识别一般有手的检测和姿态估计、手部跟踪和手势识别等。
一、手掌检测
import cv2
import mediapipe as mp
# 初始化 MediaPipe 手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
# 初始化视频流
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# 转换为 RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 获取手部关键点
results = hands.process(rgb_frame)
# 如果检测到手部
if results.multi_hand_landmarks:
for landmarks in results.multi_hand_landmarks:
# 绘制手部关键点
mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
# 显示图像
cv2.imshow('Hand Detection', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
这段代码是一个简单的手部检测应用程序,使用了 OpenCV 和 MediaPipe 库来实时检测和绘制手部关键点。下面是对代码的详细解释:
1. 导入必要的库
import cv2
import mediapipe as mp
cv2
是 OpenCV 的 Python 接口库,用于图像处理和计算机视觉任务。mediapipe
是 Google 提供的一个跨平台框架,用于实现高效的计算机视觉任务,这里用它来做手部关键点检测。
2. 初始化 MediaPipe 手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_hands
是 MediaPipe 提供的手部检测模块。通过mp.solutions.hands
访问。hands = mp_hands.Hands()
创建了一个Hands
对象,用于进行手部检测。它会自动处理图像中的手部检测、手部关键点识别和跟踪。
3. 初始化视频流
cap = cv2.VideoCapture(0)
cv2.VideoCapture(0)
打开了计算机的默认摄像头(0
表示第一个摄像头)。返回的cap
对象用于读取视频流。
4. 开始处理视频流
while True:
ret, frame = cap.read()
if not ret:
break
cap.read()
从摄像头读取一帧图像并返回两个值:ret
和frame
。ret
是布尔值,表示图像是否成功读取,frame
是当前帧的图像。- 如果
ret
为False
,则说明读取图像失败,程序退出循环。
5. 转换图像为 RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
- OpenCV 使用 BGR(蓝绿红)色彩空间,而 MediaPipe 要求输入图像为 RGB(红绿蓝)色彩空间。因此,我们用
cv2.cvtColor
将图像从 BGR 转换为 RGB。
6. 处理图像以检测手部关键点
results = hands.process(rgb_frame)
hands.process(rgb_frame)
将 RGB 图像传递给 MediaPipe 的Hands
模型进行处理。results
包含了检测到的手部信息,包括手部关键点的位置。如果图像中没有手部,results.multi_hand_landmarks
会是空的。
7. 绘制手部关键点
if results.multi_hand_landmarks:
for landmarks in results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
results.multi_hand_landmarks
是一个包含所有检测到的手的关键点位置的列表。如果检测到手部,它会返回一个非空列表。landmarks
是每个手部的关键点集合,每个关键点是一个(x, y, z)
坐标。mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
将每个手的关键点绘制到frame
图像上,并且连接关键点,形成手的骨架结构。mp_hands.HAND_CONNECTIONS
用于连接不同的关键点。
8. 显示处理后的图像
cv2.imshow('Hand Detection', frame)
cv2.imshow
显示处理后的图像(frame
)。窗口的标题是 ‘Hand Detection’。
9. 按键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.waitKey(1)
等待键盘输入,1
表示等待 1 毫秒。返回值是按下键的 ASCII 码。- 如果按下
q
键,ord('q')
的值与cv2.waitKey(1)
返回值相等,程序就会退出循环,停止视频流。
10. 释放资源并关闭窗口
cap.release()
cv2.destroyAllWindows()
cap.release()
释放摄像头资源。cv2.destroyAllWindows()
关闭所有 OpenCV 显示的窗口。
二、手关键点估计
要进行手关键点估计,可以使用 MediaPipe 库来进行高效的手部检测,结合 OpenCV 来进行图像处理和显示。这里将展示一个基于 MediaPipe 手部模型的手关键点估计的完整代码。
MediaPipe 提供了一个预训练的模型来检测手部的 21 个关键点,包括手指关节和手掌部位。我们将使用 MediaPipe 提供的 Hands
模块来检测手部关键点。
import cv2
import mediapipe as mp
# 初始化 MediaPipe 手部检测模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取每一帧图像
ret, frame = cap.read()
if not ret:
print("无法获取视频帧")
break
# 将图像转换为 RGB 格式,MediaPipe 需要 RGB 输入
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 获取手部关键点检测结果
results = hands.process(rgb_frame)
# 如果检测到手部
if results.multi_hand_landmarks:
for landmarks in results.multi_hand_landmarks:
# 绘制手部关键点和连接线
mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
# 获取并显示每个关键点的坐标
for idx, landmark in enumerate(landmarks.landmark):
h, w, c = frame.shape
cx, cy = int(landmark.x * w), int(landmark.y * h)
cv2.putText(frame, str(idx), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1) # 绘制红色的关键点
# 显示图像
cv2.imshow('Hand Keypoint Estimation', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头资源并关闭窗口
cap.release()
cv2.destroyAllWindows()
1.代码解释
-
初始化 MediaPipe 手部模型:
- 使用
mp.solutions.hands.Hands()
创建手部检测模型,min_detection_confidence=0.7
和min_tracking_confidence=0.5
表示最低的检测和追踪置信度阈值,适用于动态环境中。
- 使用
-
打开摄像头:
- 使用
cv2.VideoCapture(0)
打开计算机的默认摄像头,并获取实时视频流。
- 使用
-
图像转换:
- OpenCV 默认使用 BGR 色彩空间,但 MediaPipe 需要 RGB 色彩空间,因此使用
cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
将图像转换为 RGB 格式。
- OpenCV 默认使用 BGR 色彩空间,但 MediaPipe 需要 RGB 色彩空间,因此使用
-
处理图像并获取手部关键点:
hands.process(rgb_frame)
用于处理图像并检测手部关键点。results
对象包含手部信息,如果检测到手部,results.multi_hand_landmarks
将返回手部的 21 个关键点的位置。
-
绘制关键点和连接线:
mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
绘制手的 21 个关键点以及它们之间的连接线(即手指的骨架)。landmarks.landmark
包含了每个关键点的(x, y, z)
坐标,其中x
和y
是图像中的像素坐标,z
是深度信息(在 3D 空间中的位置,但对于 2D 图像可忽略)。
-
显示关键点坐标:
- 对于每个关键点,通过
cv2.putText
在图像上显示关键点的索引。cv2.circle
用来在图像上绘制关键点位置。
- 对于每个关键点,通过
-
显示视频流:
- 使用
cv2.imshow()
来显示处理过的每一帧图像,展示检测到的手部关键点。
- 使用
-
退出条件:
- 按下
q
键退出程序并释放摄像头资源。
- 按下
2.手部关键点索引
MediaPipe 的 Hands
模型检测手部的 21 个关键点,索引如下(从 0 到 20):
- 0: 手腕
- 1: 大拇指根部
- 2: 大拇指第一关节
- 3: 大拇指第二关节
- 4: 大拇指尖端
- 5: 食指根部
- 6: 食指第一关节
- 7: 食指第二关节
- 8: 食指尖端
- 9: 中指根部
- 10: 中指第一关节
- 11: 中指第二关节
- 12: 中指尖端
- 13: 无名指根部
- 14: 无名指第一关节
- 15: 无名指第二关节
- 16: 无名指尖端
- 17: 小指根部
- 18: 小指第一关节
- 19: 小指第二关节
- 20: 小指尖端
三、实例
YOLO(You Only Look Once)是一个非常强大的目标检测算法,特别适用于实时应用。它将目标检测任务转化为一个回归问题,能够快速且准确地检测图像中的目标物体。虽然 YOLO 本身并没有直接针对手部关键点估计的功能,但可以用它来检测手部区域,然后结合其他模型(比如 MediaPipe 或深度学习的关键点检测模型)来进行手关键点估计。
- YOLO 检测手部:首先用 YOLO 模型检测图像中的手部位置,YOLO 会输出手部的边界框。
- 手部关键点估计:然后,我们可以通过一个手部关键点估计模型(如 MediaPipe 或自定义深度学习模型)来进一步估计手部的 21 个关键点。
import cv2
import torch
import mediapipe as mp
# 初始化 MediaPipe 手部检测模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
# 加载 YOLOv5 模型(选择合适的模型大小)
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # 'yolov5s' 是一个小型的 YOLOv5 模型
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取每一帧图像
ret, frame = cap.read()
if not ret:
print("无法获取视频帧")
break
# 使用 YOLOv5 检测图像中的物体(包括手部)
results = model(frame) # 检测结果,包括边界框、标签、置信度等
# 提取 YOLOv5 检测到的手部(通常手部被标记为 0 或其他标签,依赖于训练数据)
# 获取所有检测到的物体的边界框
hands_detections = results.xyxy[0] # 获取检测结果,xyxy 形式 [x_min, y_min, x_max, y_max, confidence, class]
for detection in hands_detections:
if detection[5] == 0: # 类别 0 代表手部(具体根据训练数据集而定)
x_min, y_min, x_max, y_max = map(int, detection[:4])
# 在图像上绘制手部边界框
cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (255, 0, 0), 2)
# 提取手部区域并进行手部关键点估计
hand_region = frame[y_min:y_max, x_min:x_max]
rgb_hand_region = cv2.cvtColor(hand_region, cv2.COLOR_BGR2RGB)
# 使用 MediaPipe 估计手部关键点
hand_results = hands.process(rgb_hand_region)
# 如果检测到手部关键点
if hand_results.multi_hand_landmarks:
for landmarks in hand_results.multi_hand_landmarks:
# 绘制手部关键点和连接线
mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
for idx, landmark in enumerate(landmarks.landmark):
h, w, c = frame.shape
cx, cy = int(landmark.x * w), int(landmark.y * h)
cv2.putText(frame, str(idx), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1) # 绘制红色的关键点
# 显示图像
cv2.imshow('Hand Detection and Keypoint Estimation', frame)
# 按 'q' 键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放摄像头资源并关闭窗口
cap.release()
cv2.destroyAllWindows()
1.代码解析
- 加载 YOLOv5 模型:
torch.hub.load('ultralytics/yolov5', 'yolov5s')
载入 YOLOv5 模型,yolov5s
是一个小型的 YOLOv5 模型,适合实时应用。可以根据需求选择yolov5m
或yolov5l
(更大的模型,精度更高但速度较慢)。
- 读取摄像头视频流:
- 使用
cv2.VideoCapture(0)
打开摄像头并读取每一帧图像。
- 使用
- YOLOv5 物体检测:
- 使用
model(frame)
进行图像检测,返回一个results
对象,其中包含检测到的边界框、类别标签、置信度等信息。
- 使用
- 提取手部区域:
- 如果 YOLO 检测到手部(通过检测类别标号),提取手部的区域并将其转化为 RGB 格式(因为 MediaPipe 需要 RGB 输入)。
- MediaPipe 手部关键点估计:
- 使用
hands.process(rgb_hand_region)
进行手部关键点估计,返回手部的关键点信息。 - 使用
mp_drawing.draw_landmarks()
绘制手部的 21 个关键点和连接线。
- 使用
- 结果显示:
- 将检测到的手部边界框、关键点及连接线显示在视频流上。
- 退出条件:
- 按下
q
键退出程序。
- 按下