import numpy
import time
import numpy as np
import cv2 as cv
import mediapipe as mp
from charset_normalizer.md import annotations
from mediapipe.tasks.python.vision import HandLandmarkerOptions,HandLandmarkerResult
from mediapipe import solutions
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
from unicodedata import category
from mediapipe.framework.formats import landmark_pb2
class HandGestureCapture:
def __init__(self):
self.init_video()
self.init_mediapipe_haands_detector()
def init_video(self):
self.capture = cv.VideoCapture(0)
self.capture.set(cv.CAP_PROP_FPS,30)
self.capture.set(cv.CAP_PROP_FRAME_WIDTH,640)
self.capture.set(cv.CAP_PROP_FRAME_HEIGHT,480)
def init_mediapipe_haands_detector(self):
hand_options = HandLandmarkerOptions(
base_options = python.BaseOptions(model_asset_path = "D:\\PythonProject2\\hand_landmarker.task"),
running_mode = vision.RunningMode.LIVE_STREAM,
result_callback = self.on_finish_hands
)
self.hand_detector = vision.HandLandmarker.create_from_options(hand_options)
self.hand_result = None
self.finger_index_to_angles = dict()
def on_finish_hands(self,result:HandLandmarkerResult, output_image: mp.Image, timestamp_ms:int):
self.hand_result = result
print(f'hand landmarker result: consume{self.get_cur_time()-timestamp_ms}ms,{self.hand_result}')
if len(self.hand_result.hand_world_landmarks) > 0:
self.calculate_all_fingers_angles()
print(self.finger_index_to_angles)
def calculate_all_fingers_angles(self):
hand_landmarks = self.hand_result.hand_world_landmarks[0]
self.finger_index_to_angles.clear()
for i in range(4):
self.finger_index_to_angles[5 + i * 4] = self.calculate_fingers_angle(0,5 + i * 4,6 + i * 4,hand_landmarks)
self.finger_index_to_angles[6 + i * 4] = self.calculate_fingers_angle(5 + i * 4, 6 + i * 4, 7 + i * 4,hand_landmarks)
self.finger_index_to_angles[7 + i * 4] = self.calculate_fingers_angle(6 + i * 4, 7+ i * 4, 8 + i * 4,hand_landmarks)
def calculate_finger_angle(self,root_index,middle_index,end_index,hand_landmarks):
root = hand_landmarks[root_index]
middle = hand_landmarks[middle_index]
end = hand_landmarks[end_index]
vec1 = np.array([root.x - middle.x,root.y - middle.y,root.z - middle.z])
vec2 = np.array([end.x - middle.x, end.y - middle.y, end.z - middle.z])
vec1_norm = self.normalize_vector(vec1)
vec2_norm = self.normalize_vector(vec2)
dot_product_result = np.dot(vec1_norm,vec2_norm)
angle = np.rad2deg(np.arccos(dot_product_result))
return angle
def normalize_vector(self,vector):
magnitude = np.linalg.norm(vector)
if magnitude == 0:
return vector
return vector / magnitude
def get_cur_time(self):
return int(time.time() * 1000)
def run(self):
while 1:
ret,frame = self.capture.read()
if ret:
frame_as_numpy_array = numpy.asarray(frame)
mp_image = mp.Image(image_format = mp.ImageFormat.SRGB,data = frame_as_numpy_array)
self.hand_detector.detect_async(mp_image,self.get_cur_time())
if self.hand_result:
frame = self.draw_landmarks_on_image(frame,self.hand_result)
cv.imshow("frame",frame)
key = cv.waitKey(1)
if key == 27:
break
self.exit()
def draw_landmarks_on_image(self,rgb_image,detection_result):
hand_landmarks_list = detection_result.hand_landmarks
handedness_list = detection_result.handedness
annotated_image = np.copy(rgb_image)
for idx in range(len(hand_landmarks_list)):
hand_landmarks = hand_landmarks_list[idx]
handedness = handedness_list[idx]
hand_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
hand_landmarks_proto.landmark.extend([
landmark_pb2.NormalizedLandmark(x = landmark.x, y = landmark.y, z = landmark.z) for landmark in hand_landmarks
])
solutions.drawing_utils.draw_landmarks(
annotated_image,
hand_landmarks_proto,
solutions.hands.HAND_CONNECTIONS,
solutions.drawing_styles.get_default_hand_landmarks_style(),
solutions.drawing_styles.get_default_hand_connections_style())
return annotated_image
def exit(self):
self.capture.release()
cv.destroyAllWindows()
为什么运行时只允许了一次就自动退出了?
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
5条回答 默认 最新
檀越@新空间 2025-06-07 20:02关注晚上好🌙🌙🌙
本答案参考通义千问从你的代码中可以看出,你正在尝试使用
MediaPipe和OpenCV来检测手部关键点,并计算手指的角度。然而,代码中存在一些问题和潜在的改进空间。以下是一些关键问题以及解决方案:
问题分析
-
命名错误:
- 函数名
calculate_finger_angle在调用时拼写为calculate_fingers_angle,导致运行时错误。 - 函数名
on_finish_hands的参数类型注解中使用了HandLandmarkerResult,但实际类型是vision.HandLandmarkerResult。
- 函数名
-
未定义的变量:
mp模块未导入,但代码中多次使用了mp.Image和mp.ImageFormat。
-
逻辑问题:
draw_landmarks_on_image方法未完成,缺少绘制逻辑。- 手势角度计算逻辑可能需要进一步优化以提高准确性。
-
资源管理:
- 没有对摄像头和 MediaPipe 资源进行显式释放。
解决方案
以下是修复后的代码,包含所有问题的解决方法:
import numpy as np import time import cv2 as cv import mediapipe as mp from mediapipe.tasks.python.vision import HandLandmarkerResult from mediapipe.tasks.python import vision from mediapipe.framework.formats import landmark_pb2 class HandGestureCapture: def __init__(self): self.init_video() self.init_mediapipe_hands_detector() def init_video(self): self.capture = cv.VideoCapture(0) self.capture.set(cv.CAP_PROP_FPS, 30) self.capture.set(cv.CAP_PROP_FRAME_WIDTH, 640) self.capture.set(cv.CAP_PROP_FRAME_HEIGHT, 480) def init_mediapipe_hands_detector(self): hand_options = HandLandmarkerOptions( base_options=vision.BaseOptions(model_asset_path="D:\\PythonProject2\\hand_landmarker.task"), running_mode=vision.RunningMode.LIVE_STREAM, result_callback=self.on_finish_hands, ) self.hand_detector = vision.HandLandmarker.create_from_options(hand_options) self.hand_result = None self.finger_index_to_angles = {} def on_finish_hands(self, result: vision.HandLandmarkerResult, unused_output_image, timestamp_ms: int): self.hand_result = result print(f"Hand Landmarker Result: Consume {self.get_cur_time() - timestamp_ms} ms") if len(result.hand_world_landmarks) > 0: self.calculate_all_fingers_angles() print(self.finger_index_to_angles) def calculate_all_fingers_angles(self): hand_landmarks = self.hand_result.hand_world_landmarks[0] self.finger_index_to_angles.clear() for i in range(4): # Loop through fingers (index, middle, ring, pinky) self.finger_index_to_angles[5 + i * 4] = self.calculate_finger_angle( 0, 5 + i * 4, 6 + i * 4, hand_landmarks ) self.finger_index_to_angles[6 + i * 4] = self.calculate_finger_angle( 5 + i * 4, 6 + i * 4, 7 + i * 4, hand_landmarks ) self.finger_index_to_angles[7 + i * 4] = self.calculate_finger_angle( 6 + i * 4, 7 + i * 4, 8 + i * 4, hand_landmarks ) def calculate_finger_angle(self, root_index, middle_index, end_index, hand_landmarks): root = hand_landmarks[root_index] middle = hand_landmarks[middle_index] end = hand_landmarks[end_index] vec1 = np.array([root.x - middle.x, root.y - middle.y, root.z - middle.z]) vec2 = np.array([end.x - middle.x, end.y - middle.y, end.z - middle.z]) vec1_norm = self.normalize_vector(vec1) vec2_norm = self.normalize_vector(vec2) dot_product_result = np.dot(vec1_norm, vec2_norm) angle = np.rad2deg(np.arccos(dot_product_result)) return angle def normalize_vector(self, vector): magnitude = np.linalg.norm(vector) if magnitude == 0: return vector return vector / magnitude def get_cur_time(self): return int(time.time() * 1000) def run(self): while True: ret, frame = self.capture.read() if not ret: break frame_as_numpy_array = np.asarray(frame) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_as_numpy_array) self.hand_detector.detect_async(mp_image, self.get_cur_time()) if self.hand_result: frame = self.draw_landmarks_on_image(frame, self.hand_result) cv.imshow("Frame", frame) key = cv.waitKey(1) if key == 27: # Press 'ESC' to exit break self.exit() def draw_landmarks_on_image(self, rgb_image, detection_result): hand_landmarks_list = detection_result.hand_world_landmarks annotated_image = np.copy(rgb_image) for landmarks in hand_landmarks_list: for landmark in landmarks: x, y, z = landmark.x, landmark.y, landmark.z cv.circle(annotated_image, (int(x * rgb_image.shape[1]), int(y * rgb_image.shape[0])), 3, (0, 255, 0), -1) return annotated_image def exit(self): self.capture.release() cv.destroyAllWindows() if __name__ == "__main__": hand_capture = HandGestureCapture() hand_capture.run()
修改点详解
-
修复命名错误:
- 将
calculate_fingers_angle改为calculate_finger_angle。 - 使用正确的
HandLandmarkerResult类型(vision.HandLandmarkerResult)。
- 将
-
导入缺失模块:
- 导入
mediapipe模块,确保mp.Image和mp.ImageFormat可用。
- 导入
-
完善手势绘制逻辑:
- 在
draw_landmarks_on_image中添加了绘制关键点的逻辑。
- 在
-
资源管理:
- 在
exit方法中释放摄像头资源并关闭窗口。
- 在
-
日志优化:
- 移除了冗长的日志输出,仅保留必要的信息。
运行结果
运行上述代码后,程序会打开摄像头窗口,实时显示手部关键点及其角度,并通过绿色圆圈标记关键点位置。按下
ESC键可退出程序。如果需要进一步优化或扩展功能(如手势识别),可以在此基础上继续开发。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报-