各位同学好,今天和大家分享一下如何使用MediaPipe完成手部关键点实时检测跟踪。先放张图看效果,15代表FPS值。

# 安装opencvpip install opencv-contrib-python# 安装mediapipepip install mediapipe# pip install mediapipe --user #有user报错的话试试这个 # 安装之后导入各个包import cv2 #opencvimport mediapipe as mpimport timeMediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。它能够完成人脸识别,虹膜检测,体态跟踪等。今天我们介绍一下手部关键点检测,剩下的后续章节讲述,感兴趣的点个关注呦。

参数:
static_image_mode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有 max_num_hands 手并定位了相应的手的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一只手的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行手部检测,用于处理一批静态的、可能不相关的图像。
max_num_hands: 最多检测几只手,默认为2
min_detection_confidence: 手部检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5
min_tracking_confidence: 坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 static_image_mode 为真,则忽略这个参数,手部检测将在每个图像上运行。默认为 0.5
返回值:
MULTI_HAND_LANDMARKS: 被检测/跟踪的手的集合,其中每只手被表示为21个手部地标的列表,每个地标由x, y, z组成。x和y分别由图像的宽度和高度归一化为[0,1]。Z表示地标深度。
MULTI_HANDEDNESS: 被检测/追踪的手是左手还是右手的集合。每只手由label(标签)和score(分数)组成。 label 是 'Left' 或 'Right' 值的字符串。 score 是预测左右手的估计概率。
参数:
image: 需要画图的原始图片
landmark_list: 检测到的手部关键点坐标
connections: 连接线,需要把那些坐标连接起来
landmark_drawing_spec: 坐标的颜色,粗细
connection_drawing_spec: 连接线的粗细,颜色等

由于读入视频图像通道一般为RGB,而opencv中图像通道的格式为BGR,因此需要cv2.cvtColor()函数将opencv读入的视频图像转为RGB格式。results中存放每个关键点的xyz坐标,通过.multi_hand_landmarks读取出来。
#(1)视频捕获cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头 #(2)创建检测手部关键点的方法mpHands = mp.solutions.hands #接收方法hands = mpHands.Hands(static_image_mode=False, #静态追踪,低于0.5置信度会再一次跟踪 max_num_hands=2, # 最多有2只手 min_detection_confidence=0.5, # 最小检测置信度 min_tracking_confidence=0.5) # 最小跟踪置信度 # 创建检测手部关键点和关键点之间连线的方法mpDraw = mp.solutions.drawing_utils # 查看时间pTime = 0 #处理一张图像前的时间cTime = 0 #一张图处理完的时间 #(3)处理视频图像while True: # 对每一帧视频图像处理 # 返回是否读取成功和读取的图像 success, img = cap.read() # 在循环中发送rgb图像到hands中,opencv中图像默认是BGR格式 imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 把图像传入检测模型,提取信息 results = hands.process(imgRGB) # 检查是否检测到什么东西了,没有检测到手就返回None # print(results.multi_hand_landmarks) # 检查每帧图像是否有多只手,一一提取它们 if results.multi_hand_landmarks: #如果没有手就是None for handlms in results.multi_hand_landmarks: # 绘制每只手的关键点 mpDraw.draw_landmarks(img, handlms, mpHands.HAND_CONNECTIONS) #传入想要绘图画板img,单只手的信息handlms # mpHands.HAND_CONNECTIONS绘制手部关键点之间的连线 # 记录执行时间 cTime = time.time() # 计算fps fps = 1/(cTime-pTime) # 重置起始时间 pTime = cTime # 把fps显示在窗口上;img画板;取整的fps值;显示位置的坐标;设置字体;字体比例;颜色;厚度 cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3) # 显示图像 cv2.imshow('Image', img) #窗口名,图像变量 if cv2.waitKey(1) & 0xFF==27: #每帧滞留1毫秒后消失;ESC键退出 break # 释放视频资源cap.release()cv2.destroyAllWindows()这里设置了最大可识别2只手,如果有需要可通过result.multi_handedness,分别处理左右手的坐标。

上面我们通过results.multi_hand_landmarks得到的xyz坐标是归一化后的比例坐标,即某一像素点在图像的某一比例位置[0.5, 0.5]。我们需要将其转为像素坐标,如[200,200],像素坐标一定是整数。通过图像宽度乘以比例即可得到像素长度。为了能更明显的显示关键点,把关键点画的大一些,只需以关键点的像素坐标为圆心画圆cv2.circle()即可。
因此我们在上面的代码中补充
#(1)视频捕获cap = cv2.VideoCapture(0) # 0代表电脑自带的摄像头 #(2)创建检测手部关键点的方法mpHands = mp.solutions.hands #接收方法hands = mpHands.Hands(static_image_mode=False, #静态追踪,低于0.5置信度会再一次跟踪 max_num_hands=2, # 最多有2只手 min_detection_confidence=0.5, # 最小检测置信度 min_tracking_confidence=0.5) # 最小跟踪置信度 # 创建检测手部关键点和关键点之间连线的方法mpDraw = mp.solutions.drawing_utils # 查看时间pTime = 0 #处理一张图像前的时间cTime = 0 #一张图处理完的时间 # 存放坐标信息lmList = [] #(3)处理视频图像# 文件设为True,对每一帧视频图像处理while True: # 返回是否读取成功和读取的图像 success, img = cap.read() # 在循环中发送rgb图像到hands中,opencv中图像默认是BGR格式 imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 把图像传入检测模型,提取信息 results = hands.process(imgRGB) # 检查每帧图像是否有多只手,一一提取它们 if results.multi_hand_landmarks: #如果没有手就是None for handlms in results.multi_hand_landmarks: # 获取每个关键点的索引和坐标 for index, lm in enumerate(handlms.landmark): # 索引为0代表手底部中间部位,为4代表手指关键或指尖 # print(index, lm) # 输出21个手部关键点的xyz坐标(0-1之间),是相对于图像的长宽比例 # 只需使用x和y查找位置信息 # 将xy的比例坐标转换成像素坐标 h, w, c = img.shape # 分别存放图像长\宽\通道数 # 中心坐标(小数),必须转换成整数(像素坐标) cx ,cy = int(lm.x * w), int(lm.y * h) #比例坐标x乘以宽度得像素坐标 # 打印显示21个关键点的像素坐标 print(index, cx, cy) # 存储坐标信息 lmList.append([index, cx, cy]) # 在21个关键点上换个圈,img画板,坐标(cx,cy),半径5,蓝色填充 cv2.circle(img, (cx,cy), 12, (0,0,255), cv2.FILLED) # 绘制每只手的关键点 mpDraw.draw_landmarks(img, handlms, mpHands.HAND_CONNECTIONS) #传入想要绘图画板img,单只手的信息handlms # mpHands.HAND_CONNECTIONS绘制手部关键点之间的连线 # 记录执行时间 cTime = time.time() # 计算fps fps = 1/(cTime-pTime) # 重置起始时间 pTime = cTime # 把fps显示在窗口上;img画板;取整的fps值;显示位置的坐标;设置字体;字体比例;颜色;厚度 cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3) # 显示图像 cv2.imshow('Image', img) #窗口名,图像变量 if cv2.waitKey(1) & 0xFF==27: #每帧滞留1毫秒后消失 break # 释放视频资源cap.release()cv2.destroyAllWindows() 得到的结果如下,fps=19,右下输出框中打印每帧图像的21个手部关键点xy坐标,并保存在列表中。

免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删