一. 引言 MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。在谷歌,一系列重要产品,如 YouTube、Google Lens、ARCore、Google Home 以及 Nest,都已深度整合了 MediaPipe。
MediaPipe大有用武之地,可以做物体检测、自拍分割、头发分割、人脸检测、手部检测、运动追踪,等等。基于此可以实现更高级的功能。
二. 怎么做 根据官方文档自己尝试理解也有一些收获,在这里记录一下。
1、官方文档地址Mediapipe
2、实验环境
1 2 3 4 I.win10 II. Pycharm2021 III. Python3.8 IV. mediapipe0.89
3、我需要检测人体骨架和手部,那么先构建这样的检测模型。根据官网的例子,mediapipe.solutions下有我们需要的解决方案,来看看。
1 2 3 4 import mediapipe as mpmp_holistic = mp.solutions.holistic help (mp.solutions)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Help on package mediapipe.python.solutions in mediapipe.python: NAME mediapipe.python.solutions - MediaPipe Solutions Python API. PACKAGE CONTENTS download_utils drawing_styles drawing_utils drawing_utils_test face_detection face_detection_test face_mesh face_mesh_connections face_mesh_test hands hands_connections hands_test holistic holistic_test objectron objectron_test pose pose_connections pose_test selfie_segmentation selfie_segmentation_test
以上就是mediapipe提供的解决方案,其中drawing_utils是画图用的,drawing_styles应该是渲染风格,face_detection用于面部检测,face_mesh用于绘人脸面网,hands用于手部检测,holistic是整体的解决方案(包括人脸、骨架、手),pose是识别姿势,objectron用于目标检测,selfie_segmentation是自拍分割。
4、有了方法,根据官网的例子,先导入必要的包,然后建立模型。
1 2 3 4 5 6 import cv2import mediapipe as mpmp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solutions.drawing_styles mp_holistic = mp.solutions.holistic
5、接着打开摄像头,并建立我们的类。
1 2 3 4 5 6 7 8 9 10 cap = cv2.VideoCapture(0 ) with mp_holistic.Holistic( min_detection_confidence=0.5 , min_tracking_confidence=0.5 ) as holistic: while cap.isOpened(): success, image = cap.read() if not success: print ("Ignoring empty camera frame." ) continue
先看看mp_holistic.Holistic下有什么参数
1 2 3 4 5 Methods defined here: | __init__(self, static_image_mode=False , model_complexity=1 , smooth_landmarks=True , enable_segmentation=False , smooth_segmentation=True , refine_face_landmarks=False , min_detection_confidence=0.5 , min_tracking_confidence=0.5 )
官网解释如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static_image_mode 如果设置为false,则解决方案将输入图像视为视频流。它将尝试在第一张图像中检测最突出的人,并在成功检测后进一步定位姿势和其他地标。在随后的图像中,它只是简单地跟踪那些地标,而不会调用另一个检测,直到它失去跟踪,以减少计算和延迟。如果设置为true,则人物检测会运行每个输入图像,非常适合处理一批静态的、可能不相关的图像。默认为false. model_complexity 姿势地标模型的复杂度:0 ,1 或2 。地标准确性以及推理延迟通常随模型复杂性而增加。默认为1. smooth_landmarks 如果设置为true,解决方案过滤器会在不同的输入图像之间设置地标以减少抖动,但如果static_image_mode也设置为,则忽略true。默认为true. enable_segmentation 如果设置为true,除了姿势、面部和手部地标之外,该解决方案还会生成分割掩码。默认为false. smooth_segmentation 如果设置为true,该解决方案会过滤不同输入图像的分割掩码以减少抖动。如果enable_segmentation为false或static_image_mode为 ,则忽略true。默认为true. refine_face_landmarks 是否进一步细化眼睛和嘴唇周围的地标坐标,并在虹膜周围输出额外的地标。默认为false. min_detection_confidence [0.0 , 1.0 ]来自人员检测模型的最小置信值 ( ),用于将检测视为成功。默认为0.5 . min_tracking_confidence [0.0 , 1.0 ]来自地标跟踪模型的最小置信值(将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果static_image_mode是true,则忽略,其中人员检测仅在每个图像上运行。默认为0.5 .
6、有了这些解释,就比较好理解了。然后对输入的视频流做一些预处理。
1 2 3 4 5 image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = holistic.process(image) image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
比较重要的是这一步results = holistic.process(image),前面建立了整体检测的类,类下方法process()会处理图片并返回我们要的坐标,来help看一看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Help on function process in module mediapipe.python.solutions.holistic: process(self, image: numpy.ndarray) -> <class 'NamedTuple' > Processes an RGB image and returns the pose landmarks, left and right hand landmarks, and face landmarks on the most prominent person detected. Args: image: An RGB image represented as a numpy ndarray. Raises: RuntimeError: If the underlying graph throws any error. ValueError: If the input image is not three channel RGB. Returns: A NamedTuple with fields describing the landmarks on the most prominate person detected: 1 ) "pose_landmarks" field that contains the pose landmarks. 2 ) "pose_world_landmarks" field that contains the pose landmarks in real-world 3D coordinates that are in meters with the origin at the center between hips. 3 ) "left_hand_landmarks" field that contains the left-hand landmarks. 4 ) "right_hand_landmarks" field that contains the right-hand landmarks. 5 ) "face_landmarks" field that contains the face landmarks. 6 ) "segmentation_mask" field that contains the segmentation mask if "enable_segmentation" is set to true.
7、可以看到Returns下左右手、姿势、脸部的地标都能返回,待会我就从这获取坐标。处理完图片,就需要在人体关节点上渲染。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 mp_drawing.draw_landmarks( image, results.face_landmarks, mp_holistic.FACEMESH_CONTOURS, landmark_drawing_spec=None , connection_drawing_spec=mp_drawing_styles .get_default_face_mesh_contours_style()) mp_drawing.draw_landmarks( image, results.pose_landmarks, mp_holistic.POSE_CONNECTIONS, landmark_drawing_spec=mp_drawing_styles .get_default_pose_landmarks_style()) mp_drawing.draw_landmarks(image, results.left_hand_landmarks, mp_holistic.HAND_CONNECTIONS) mp_drawing.draw_landmarks(image, results.right_hand_landmarks, mp_holistic.HAND_CONNECTIONS)
前面mp_drawing=mp.solutions.drawing_utils,draw_landmarks()参数比较多,我挑几个重要的翻译出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 draw_landmarks参数: image:表示为 numpy ndarray 的三通道 RGB 图像。 地标列表:要在其上注释的规范化地标列表原始消息 图片。 connections:地标索引元组列表,指定地标如何 在图中连接。 Landmark_drawing_spec:DrawingSpec 对象或来自的映射 将地标传递给指定地标绘图的 DrawingSpecs 颜色、线条粗细和圆半径等设置。 如果此参数明确设置为 None ,则不会绘制任何地标。 connection_drawing_spec:DrawingSpec 对象或来自的映射 到 DrawingSpecs 的手连接,它指定了 连接的绘图设置,例如颜色和线条粗细。 如果此参数明确设置为 None ,则没有地标连接 被画下来。
前面说到results = holistic.process(image)返回了结点坐标,现在来获取它。
1 2 3 if results.right_hand_landmarks: for index, landmarks in enumerate (results.right_hand_landmarks.landmark): print (index,landmarks )
解释一下,index是索引,即上图里每个手指节点的索引0,1,2,3等等,landmarks 是结点地标(x,y,z),是真实世界的3d坐标,原点位于手的近似几何中心。最后打开窗口,就可以愉快地使用mediapipe了
1 2 3 4 cv2.imshow('MediaPipe Holistic' , cv2.flip(image, 1 )) if cv2.waitKey(5 ) & 0xFF == 27 : break cap.release()
三. 运行结果 还是挺成功的,只要我右手出现在摄像头里,坐标就会输出,右手放下去就没有输出。
当然,如果想输出左手,甚至是嘴唇、鼻子、肩膀等地的坐标都是可以的。
1 2 print (results.pose_landmarks.landmark[mp_holistic.PoseLandmark.NOSE])
四. 完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import cv2import mediapipe as mpmp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solutions.drawing_styles mp_holistic = mp.solutions.holistic cap = cv2.VideoCapture(0 ) with mp_holistic.Holistic( min_detection_confidence=0.5 , min_tracking_confidence=0.5 ) as holistic: while cap.isOpened(): success, image = cap.read() if not success: print ("Ignoring empty camera frame." ) continue image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = holistic.process(image) image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) mp_drawing.draw_landmarks( image, results.face_landmarks, mp_holistic.FACEMESH_CONTOURS, landmark_drawing_spec=None , connection_drawing_spec=mp_drawing_styles .get_default_face_mesh_contours_style()) mp_drawing.draw_landmarks( image, results.pose_landmarks, mp_holistic.POSE_CONNECTIONS, landmark_drawing_spec=mp_drawing_styles .get_default_pose_landmarks_style()) mp_drawing.draw_landmarks(image, results.left_hand_landmarks, mp_holistic.HAND_CONNECTIONS) mp_drawing.draw_landmarks(image, results.right_hand_landmarks, mp_holistic.HAND_CONNECTIONS) if results.right_hand_landmarks: for index, landmarks in enumerate (results.right_hand_landmarks.landmark): print (index,landmarks ) cv2.imshow('MediaPipe Holistic' , cv2.flip(image, 1 )) if cv2.waitKey(5 ) & 0xFF == 27 : break cap.release()