谷歌出了一个开源的、跨平台的、可定制化的机器学习解决方案工具包,给在线流媒体(当然也可以用于普通的视频、图像等)提供了机器学习解决方案。感兴趣的同学可以打开这个网址了解详情:mediapipe.dev/
它提供了手势、人体姿势、人脸、物品等识别和追踪功能,并提供了C++、Python、JavaScript等编程语言的工具包以及iOS、Android平台的解决方案,今天我们就来看一下如何使用MediaPipe提供的手势识别来写一个Python代码识别手势中的数字:0-5 ,来控制pinpong板上RGB灯。
【MediaPipe人脸检测】
MediaPipe人脸检测是一种超快速的人脸检测解决方案,具有6个界标和多人脸支持。它基于BlazeFace,BlazeFace是为移动GPU推理量身定制的轻巧且性能良好的面部检测器。检测器的超实时性能使其可应用于需要准确的关注面部区域作为其他任务特定模型(例如3D面部关键点或几何估计(例如MediaPipe Face Mesh),面部特征或表情分类,以及面部区域分割。BlazeFace使用了一个轻量级的特征提取网络,该网络受MobileNetV1 / V2的启发,但与MobileNetV1 / V2不同,MobileNetV1 / V2是从GPU修改而来的GPU友好锚定方案单发MultiBox检测器(SSD)和替代非最大抑制的改进的平局解决方案策略。有关BlazeFace的更多信息,请参见参考资料部分。
import cv2
import mediapipe as mp
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
# For webcam input:
cap = cv2.VideoCapture(0)
with mp_face_detection.FaceDetection(
min_detection_confidence=0.5) as face_detection:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
# If loading a video, use 'break' instead of 'continue'.
continue
# Flip the image horizontally for a later selfie-view display, and convert
# the BGR image to RGB.
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
# To improve performance, optionally mark the image as not writeable to
# pass by reference.
image.flags.writeable = False
results = face_detection.process(image)
# Draw the face detection annotations on the image.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.detections:
for detection in results.detections:
mp_drawing.draw_detection(image, detection)
cv2.imshow('MediaPipe Face Detection', image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
【MediaPipe面网格】
MediaPipe Face Mesh是一种脸部几何解决方案,即使在移动设备上,也可以实时估计468个3D脸部界标。它采用机器学习(ML)来推断3D表面几何形状,只需要一个摄像机输入,而无需专用的深度传感器。该解决方案利用轻量级的模型架构以及整个管线中的GPU加速,可提供对实时体验至关重要的实时性能。
此外,该解决方案与“人脸几何”模块捆绑在一起,该模块弥合了人脸界标估计与有用的实时增强现实(AR)应用程序之间的鸿沟。它建立了一个公制3D空间,并使用人脸界标屏幕位置来估计该空间内的人脸几何形状。面部几何数据由常见的3D几何图元组成,其中包括面部姿势转换矩阵和三角形面部网格。在后台,采用了一种称为Procrustes Analysis的轻量级统计分析方法来驱动健壮,高性能和可移植的逻辑。该分析在CPU上运行,并且在ML模型推断的基础上具有最小的速度/内存占用量。
import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_face_mesh = mp.solutions.face_mesh
# For webcam input:
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
cap = cv2.VideoCapture(0)
with mp_face_mesh.FaceMesh(
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as face_mesh:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
# If loading a video, use 'break' instead of 'continue'.
continue
# Flip the image horizontally for a later selfie-view display, and convert
# the BGR image to RGB.
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
# To improve performance, optionally mark the image as not writeable to
# pass by reference.
image.flags.writeable = False
results = face_mesh.process(image)
# Draw the face mesh annotations on the image.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.multi_face_landmarks:
for face_landmarks in results.multi_face_landmarks:
mp_drawing.draw_landmarks(
image=image,
landmark_list=face_landmarks,
connections=mp_face_mesh.FACE_CONNECTIONS,
landmark_drawing_spec=drawing_spec,
connection_drawing_spec=drawing_spec)
cv2.imshow('MediaPipe FaceMesh', image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
【MediaPipe手】
感知手的形状和动作的能力可能是在各种技术领域和平台上改善用户体验的重要组成部分。例如,它可以构成手语理解和手势控制的基础,并且还可以在增强现实中将数字内容和信息覆盖在物理世界之上。虽然自然而然地出现在人们手中,但是强大的实时手感知力无疑是一项具有挑战性的计算机视觉任务,因为手经常相互遮挡自己或彼此(例如手指/手掌遮挡和握手),并且缺乏高对比度模式。
MediaPipe Hands是一种高保真手和手指跟踪解决方案。它采用机器学习(ML)来从单个帧中推断出手的21个3D界标。当前的最新方法主要依靠强大的桌面环境进行推理,而我们的方法可以在手机上实现实时性能,甚至可以扩展到多只手。我们希望将这种手感功能提供给更广泛的研究和开发社区,将导致出现创造性的用例,刺激新的应用程序和新的研究途径。
import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
# For webcam input:
cap = cv2.VideoCapture(0)
with mp_hands.Hands(
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as hands:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
# If loading a video, use 'break' instead of 'continue'.
continue
# Flip the image horizontally for a later selfie-view display, and convert
# the BGR image to RGB.
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
# To improve performance, optionally mark the image as not writeable to
# pass by reference.
image.flags.writeable = False
results = hands.process(image)
# Draw the hand annotations on the image.
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp_drawing.draw_landmarks(
image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('MediaPipe Hands', image)
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
【pinpong板】
【pinpong板】
PinPong Board是一款专为学习Python语言而设计的课堂类教学主板,可以满足用户所有需求,不仅可以用来制作智能检测设备,还可以制作成无线通讯设备,实现智能家居以及人工智能等。
一、产品特点
1、PinPong Board完全兼容传统的Arduino,还在Arduino原有功能的基础上集成了2路电机驱动、OLED显示、红外接收/发射、光线传感器、声音传感器、温湿度传感器、陀螺仪等新型传感器等硬件功能。
2、WiFi与局域网相连接可以实现无线下载程序功能,让你摆脱USB线的束缚。远距离的更新程序,同一个局域网内的设备可以进行无线传输,主从机设置,甚至一个PC端同时控制局域网内的所有设备。
3、基于Python编程,提供PinPong使用教程及大量的example,方便学习使用Python驱动硬件。
4、多元化编程方式和编程平台,可依据学习情况选择Python图形化编程、Python语言编程不同的编程方式;Mind+、Python等编程平台为开发者提供了极大的自由度和支持准备。
编写一个handutil.py模块,这个handutil模块有一个HandDetector类,提供了检测手势、获取手势数据的方法。代码如下,详细解释看代码注释:
import cv2
import mediapipe as mp
class HandDetector():
'''
手势识别类
'''
def __init__(self, mode=False, max_hands=2, detection_con=0.5, track_con=0.5):
'''
初始化
:param mode: 是否静态图片,默认为False
:param max_hands: 最多几只手,默认为2只
:param detection_con: 最小检测信度值,默认为0.5
:param track_con: 最小跟踪信度值,默认为0.5
'''
self.mode = mode
self.max_hands = max_hands
self.detection_con = detection_con
self.track_con = track_con
self.hands = mp.solutions.hands.Hands(self.mode, self.max_hands, self.detection_con, self.track_con)
def find_hands(self, img, draw=True):
'''
检测手势
:param img: 视频帧图片
:param draw: 是否画出手势中的节点和连接图
:return: 处理过的视频帧图片
'''
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 处理图片,检测是否有手势,将数据存进self.results中
self.results = self.hands.process(imgRGB)
if draw:
if self.results.multi_hand_landmarks:
for handlms in self.results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(img, handlms, mp.solutions.hands.HAND_CONNECTIONS)
return img
def find_positions(self, img, hand_no=0):
'''
获取手势数据
:param img: 视频帧图片
:param hand_no: 手编号(默认第1只手)
:return: 手势数据列表,每个数据成员由id, x, y组成,代码这个手势位置编号以及在屏幕中的位置
'''
self.lmslist = []
if self.results.multi_hand_landmarks:
hand = self.results.multi_hand_landmarks[hand_no]
for id, lm in enumerate(hand.landmark):
h, w, c = img.shape
cx, cy = int(lm.x * w), int(lm.y * h)
self.lmslist.append([id, cx, cy])
return self.lmslist
编写另一个fingercount.py代码,在这个代码中,调用handutil.py的HandDetector类提供的方法,获取手势数据,每个手势数据由3个数字组成:id, x, y,分别代表手势中某个点以及这个点的x\y坐标位置。下图是手势识别中每个id对应手的部位说明。
从上图可知:4, 8, 12, 16, 20分别代表大拇指、食指、中指、无名指和小指的指尖。完整代码如下:
import cv2
from handutil import HandDetector
import numpy as np
from pinpong.board import Board,Pin,NeoPixel
NEOPIXEL_PIN = Pin.D9
PIXELS_NUM = 1 #灯数
Board("uno").begin() #初始化,选择板型和端口号,不输入端口号则进行自动识别
np = NeoPixel(Pin(NEOPIXEL_PIN), PIXELS_NUM)
# 打开摄像头
cap = cv2.VideoCapture(0)
# 创建一个手势识别对象
detector = HandDetector()
# 指尖列表,分别代表大拇指、食指、中指、无名指和小指的指尖
tip_ids = [4, 8, 12, 16, 20]
while True:
success, img = cap.read()
if success:
# 检测手势
img = detector.find_hands(img, draw=True)
# 获取手势数据
lmslist = detector.find_positions(img)
if len(lmslist) > 0:
fingers = []
for tid in tip_ids:
# 找到每个指尖的位置
x, y = lmslist[tid][1], lmslist[tid][2]
cv2.circle(img, (x, y), 10, (0, 255, 0), cv2.FILLED)
# 如果是大拇指,如果大拇指指尖x位置大于大拇指第二关节的位置,则认为大拇指打开,否则认为大拇指关闭
if tid == 4:
if lmslist[tid][1] > lmslist[tid - 1][1]:
fingers.append(1)
else:
fingers.append(0)
# 如果是其他手指,如果这些手指的指尖的y位置大于第二关节的位置,则认为这个手指打开,否则认为这个手指关闭
else:
if lmslist[tid][2] < lmslist[tid - 2][2]:
fingers.append(1)
else:
fingers.append(0)
# fingers是这样一个列表,5个数据,0代表一个手指关闭,1代表一个手指打开
# 判断有几个手指打开
cnt = fingers.count(1)
if cnt==0:
np[0] = (0, 0 ,0) #设置RGB灯为黑色
if cnt==1:
np[0] = (255, 0 ,0) #设置RGB灯为红色
if cnt==2:
np[0] = (0, 255 ,0) #设置RGB灯为绿色
if cnt==3:
np[0] = (0, 0 ,255) #设置RGB灯为蓝色
# 找到对应的手势图片并显示
cv2.rectangle(img, (200, 0), (300, 100), (0, 255, 0), cv2.FILLED)
cv2.putText(img, str(cnt), (200, 100), cv2.FONT_HERSHEY_DUPLEX, 5, (0, 0, 255))
cv2.imshow('Image', img)
k = cv2.waitKey(1)
if k == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
评论