Project import generated by Copybara.

GitOrigin-RevId: 1610e588e497817fae2d9a458093ab6a370e2972
This commit is contained in:
MediaPipe Team
2021-08-18 17:45:46 -07:00
committed by jqtang
parent b899d17f18
commit 710fb3de58
158 changed files with 10104 additions and 1568 deletions
+3
View File
@@ -224,6 +224,7 @@ class SolutionBase:
calculator_params is not allowed to be modified.
e) If the calculator options field is a repeated field but the field
value to be set is not iterable.
f) If not all calculator params are valid.
"""
if bool(binary_graph_path) == bool(graph_config):
raise ValueError(
@@ -499,6 +500,8 @@ class SolutionBase:
# have been visited.
if num_modified == len(nested_calculator_params):
break
if num_modified < len(nested_calculator_params):
raise ValueError('Not all calculator params are valid.')
def _make_packet(self, packet_data_type: _PacketDataType,
data: Any) -> packet.Packet:
+2
View File
@@ -18,7 +18,9 @@ import mediapipe.python.solutions.drawing_styles
import mediapipe.python.solutions.drawing_utils
import mediapipe.python.solutions.face_detection
import mediapipe.python.solutions.face_mesh
import mediapipe.python.solutions.face_mesh_connections
import mediapipe.python.solutions.hands
import mediapipe.python.solutions.hands_connections
import mediapipe.python.solutions.holistic
import mediapipe.python.solutions.objectron
import mediapipe.python.solutions.pose
+105 -48
View File
@@ -15,17 +15,21 @@
from typing import Mapping, Tuple
from mediapipe.python.solutions import face_mesh_connections
from mediapipe.python.solutions import hands_connections
from mediapipe.python.solutions.drawing_utils import DrawingSpec
from mediapipe.python.solutions.hands import HandLandmark
from mediapipe.python.solutions.pose import PoseLandmark
_RADIUS = 5
_RED = (54, 67, 244)
_GREEN = (118, 230, 0)
_RED = (48, 48, 255)
_GREEN = (48, 255, 48)
_BLUE = (192, 101, 21)
_YELLOW = (0, 204, 255)
_GRAY = (174, 164, 144)
_GRAY = (128, 128, 128)
_PURPLE = (128, 64, 128)
_PEACH = (180, 229, 255)
_WHITE = (224, 224, 224)
# Hands
_THICKNESS_WRIST_MCP = 3
@@ -70,60 +74,69 @@ _HAND_LANDMARK_STYLE = {
color=_BLUE, thickness=_THICKNESS_DOT, circle_radius=_RADIUS),
}
# Hand connections
_PALM_CONNECTIONS = ((HandLandmark.WRIST, HandLandmark.THUMB_CMC),
(HandLandmark.WRIST, HandLandmark.INDEX_FINGER_MCP),
(HandLandmark.MIDDLE_FINGER_MCP,
HandLandmark.RING_FINGER_MCP),
(HandLandmark.RING_FINGER_MCP, HandLandmark.PINKY_MCP),
(HandLandmark.INDEX_FINGER_MCP,
HandLandmark.MIDDLE_FINGER_MCP), (HandLandmark.WRIST,
HandLandmark.PINKY_MCP))
_THUMB_CONNECTIONS = ((HandLandmark.THUMB_CMC, HandLandmark.THUMB_MCP),
(HandLandmark.THUMB_MCP, HandLandmark.THUMB_IP),
(HandLandmark.THUMB_IP, HandLandmark.THUMB_TIP))
_INDEX_FINGER_CONNECTIONS = ((HandLandmark.INDEX_FINGER_MCP,
HandLandmark.INDEX_FINGER_PIP),
(HandLandmark.INDEX_FINGER_PIP,
HandLandmark.INDEX_FINGER_DIP),
(HandLandmark.INDEX_FINGER_DIP,
HandLandmark.INDEX_FINGER_TIP))
_MIDDLE_FINGER_CONNECTIONS = ((HandLandmark.MIDDLE_FINGER_MCP,
HandLandmark.MIDDLE_FINGER_PIP),
(HandLandmark.MIDDLE_FINGER_PIP,
HandLandmark.MIDDLE_FINGER_DIP),
(HandLandmark.MIDDLE_FINGER_DIP,
HandLandmark.MIDDLE_FINGER_TIP))
_RING_FINGER_CONNECTIONS = ((HandLandmark.RING_FINGER_MCP,
HandLandmark.RING_FINGER_PIP),
(HandLandmark.RING_FINGER_PIP,
HandLandmark.RING_FINGER_DIP),
(HandLandmark.RING_FINGER_DIP,
HandLandmark.RING_FINGER_TIP))
_PINKY_FINGER_CONNECTIONS = ((HandLandmark.PINKY_MCP, HandLandmark.PINKY_PIP),
(HandLandmark.PINKY_PIP, HandLandmark.PINKY_DIP),
(HandLandmark.PINKY_DIP, HandLandmark.PINKY_TIP))
# Hands connections
_HAND_CONNECTION_STYLE = {
_PALM_CONNECTIONS:
hands_connections.HAND_PALM_CONNECTIONS:
DrawingSpec(color=_GRAY, thickness=_THICKNESS_WRIST_MCP),
_THUMB_CONNECTIONS:
hands_connections.HAND_THUMB_CONNECTIONS:
DrawingSpec(color=_PEACH, thickness=_THICKNESS_FINGER),
_INDEX_FINGER_CONNECTIONS:
hands_connections.HAND_INDEX_FINGER_CONNECTIONS:
DrawingSpec(color=_PURPLE, thickness=_THICKNESS_FINGER),
_MIDDLE_FINGER_CONNECTIONS:
hands_connections.HAND_MIDDLE_FINGER_CONNECTIONS:
DrawingSpec(color=_YELLOW, thickness=_THICKNESS_FINGER),
_RING_FINGER_CONNECTIONS:
hands_connections.HAND_RING_FINGER_CONNECTIONS:
DrawingSpec(color=_GREEN, thickness=_THICKNESS_FINGER),
_PINKY_FINGER_CONNECTIONS:
hands_connections.HAND_PINKY_FINGER_CONNECTIONS:
DrawingSpec(color=_BLUE, thickness=_THICKNESS_FINGER)
}
# FaceMesh connections
_THICKNESS_TESSELATION = 1
_THICKNESS_CONTOURS = 2
_FACEMESH_CONTOURS_CONNECTION_STYLE = {
face_mesh_connections.FACEMESH_LIPS:
DrawingSpec(color=_WHITE, thickness=_THICKNESS_CONTOURS),
face_mesh_connections.FACEMESH_LEFT_EYE:
DrawingSpec(color=_GREEN, thickness=_THICKNESS_CONTOURS),
face_mesh_connections.FACEMESH_LEFT_EYEBROW:
DrawingSpec(color=_GREEN, thickness=_THICKNESS_CONTOURS),
face_mesh_connections.FACEMESH_RIGHT_EYE:
DrawingSpec(color=_RED, thickness=_THICKNESS_CONTOURS),
face_mesh_connections.FACEMESH_RIGHT_EYEBROW:
DrawingSpec(color=_RED, thickness=_THICKNESS_CONTOURS),
face_mesh_connections.FACEMESH_FACE_OVAL:
DrawingSpec(color=_WHITE, thickness=_THICKNESS_CONTOURS)
}
def get_default_hand_landmark_style() -> Mapping[int, DrawingSpec]:
"""Returns the default hand landmark drawing style.
# Pose
_THICKNESS_POSE_LANDMARKS = 2
_POSE_LANDMARKS_LEFT = frozenset([
PoseLandmark.LEFT_EYE_INNER, PoseLandmark.LEFT_EYE,
PoseLandmark.LEFT_EYE_OUTER, PoseLandmark.LEFT_EAR, PoseLandmark.MOUTH_LEFT,
PoseLandmark.LEFT_SHOULDER, PoseLandmark.LEFT_ELBOW,
PoseLandmark.LEFT_WRIST, PoseLandmark.LEFT_PINKY, PoseLandmark.LEFT_INDEX,
PoseLandmark.LEFT_THUMB, PoseLandmark.LEFT_HIP, PoseLandmark.LEFT_KNEE,
PoseLandmark.LEFT_ANKLE, PoseLandmark.LEFT_HEEL,
PoseLandmark.LEFT_FOOT_INDEX
])
_POSE_LANDMARKS_RIGHT = frozenset([
PoseLandmark.RIGHT_EYE_INNER, PoseLandmark.RIGHT_EYE,
PoseLandmark.RIGHT_EYE_OUTER, PoseLandmark.RIGHT_EAR,
PoseLandmark.MOUTH_RIGHT, PoseLandmark.RIGHT_SHOULDER,
PoseLandmark.RIGHT_ELBOW, PoseLandmark.RIGHT_WRIST,
PoseLandmark.RIGHT_PINKY, PoseLandmark.RIGHT_INDEX,
PoseLandmark.RIGHT_THUMB, PoseLandmark.RIGHT_HIP, PoseLandmark.RIGHT_KNEE,
PoseLandmark.RIGHT_ANKLE, PoseLandmark.RIGHT_HEEL,
PoseLandmark.RIGHT_FOOT_INDEX
])
def get_default_hand_landmarks_style() -> Mapping[int, DrawingSpec]:
"""Returns the default hand landmarks drawing style.
Returns:
A mapping from each hand landmark to the default drawing spec.
A mapping from each hand landmark to its default drawing spec.
"""
hand_landmark_style = {}
for k, v in _HAND_LANDMARK_STYLE.items():
@@ -132,15 +145,59 @@ def get_default_hand_landmark_style() -> Mapping[int, DrawingSpec]:
return hand_landmark_style
def get_default_hand_connection_style(
def get_default_hand_connections_style(
) -> Mapping[Tuple[int, int], DrawingSpec]:
"""Returns the default hand connection drawing style.
"""Returns the default hand connections drawing style.
Returns:
A mapping from each hand connection to the default drawing spec.
A mapping from each hand connection to its default drawing spec.
"""
hand_connection_style = {}
for k, v in _HAND_CONNECTION_STYLE.items():
for connection in k:
hand_connection_style[connection] = v
return hand_connection_style
def get_default_face_mesh_contours_style(
) -> Mapping[Tuple[int, int], DrawingSpec]:
"""Returns the default face mesh contours drawing style.
Returns:
A mapping from each face mesh contours connection to its default drawing
spec.
"""
face_mesh_contours_connection_style = {}
for k, v in _FACEMESH_CONTOURS_CONNECTION_STYLE.items():
for connection in k:
face_mesh_contours_connection_style[connection] = v
return face_mesh_contours_connection_style
def get_default_face_mesh_tesselation_style() -> DrawingSpec:
"""Returns the default face mesh tesselation drawing style.
Returns:
A DrawingSpec.
"""
return DrawingSpec(color=_GRAY, thickness=_THICKNESS_TESSELATION)
def get_default_pose_landmarks_style() -> Mapping[int, DrawingSpec]:
"""Returns the default pose landmarks drawing style.
Returns:
A mapping from each pose landmark to its default drawing spec.
"""
pose_landmark_style = {}
left_spec = DrawingSpec(
color=(0, 138, 255), thickness=_THICKNESS_POSE_LANDMARKS)
right_spec = DrawingSpec(
color=(231, 217, 0), thickness=_THICKNESS_POSE_LANDMARKS)
for landmark in _POSE_LANDMARKS_LEFT:
pose_landmark_style[landmark] = left_spec
for landmark in _POSE_LANDMARKS_RIGHT:
pose_landmark_style[landmark] = right_spec
pose_landmark_style[PoseLandmark.NOSE] = DrawingSpec(
color=_WHITE, thickness=_THICKNESS_POSE_LANDMARKS)
return pose_landmark_style
+27 -19
View File
@@ -26,19 +26,21 @@ from mediapipe.framework.formats import detection_pb2
from mediapipe.framework.formats import location_data_pb2
from mediapipe.framework.formats import landmark_pb2
PRESENCE_THRESHOLD = 0.5
RGB_CHANNELS = 3
_PRESENCE_THRESHOLD = 0.5
_VISIBILITY_THRESHOLD = 0.5
_RGB_CHANNELS = 3
WHITE_COLOR = (224, 224, 224)
BLACK_COLOR = (0, 0, 0)
RED_COLOR = (0, 0, 255)
GREEN_COLOR = (0, 128, 0)
BLUE_COLOR = (255, 0, 0)
VISIBILITY_THRESHOLD = 0.5
@dataclasses.dataclass
class DrawingSpec:
# Color for drawing the annotation. Default to the green color.
color: Tuple[int, int, int] = (0, 255, 0)
# Color for drawing the annotation. Default to the white color.
color: Tuple[int, int, int] = WHITE_COLOR
# Thickness for drawing the annotation. Default to 2 pixels.
thickness: int = 2
# Circle radius. Default to 2 pixels.
@@ -86,7 +88,7 @@ def draw_detection(
"""
if not detection.location_data:
return
if image.shape[2] != RGB_CHANNELS:
if image.shape[2] != _RGB_CHANNELS:
raise ValueError('Input image must contain three channel rgb data.')
image_rows, image_cols, _ = image.shape
@@ -147,15 +149,15 @@ def draw_landmarks(
"""
if not landmark_list:
return
if image.shape[2] != RGB_CHANNELS:
if image.shape[2] != _RGB_CHANNELS:
raise ValueError('Input image must contain three channel rgb data.')
image_rows, image_cols, _ = image.shape
idx_to_coordinates = {}
for idx, landmark in enumerate(landmark_list.landmark):
if ((landmark.HasField('visibility') and
landmark.visibility < VISIBILITY_THRESHOLD) or
landmark.visibility < _VISIBILITY_THRESHOLD) or
(landmark.HasField('presence') and
landmark.presence < PRESENCE_THRESHOLD)):
landmark.presence < _PRESENCE_THRESHOLD)):
continue
landmark_px = _normalized_to_pixel_coordinates(landmark.x, landmark.y,
image_cols, image_rows)
@@ -178,11 +180,18 @@ def draw_landmarks(
drawing_spec.thickness)
# Draws landmark points after finishing the connection lines, which is
# aesthetically better.
for idx, landmark_px in idx_to_coordinates.items():
drawing_spec = landmark_drawing_spec[idx] if isinstance(
landmark_drawing_spec, Mapping) else landmark_drawing_spec
cv2.circle(image, landmark_px, drawing_spec.circle_radius,
drawing_spec.color, drawing_spec.thickness)
if landmark_drawing_spec:
for idx, landmark_px in idx_to_coordinates.items():
drawing_spec = landmark_drawing_spec[idx] if isinstance(
landmark_drawing_spec, Mapping) else landmark_drawing_spec
# White circle border
circle_border_radius = max(drawing_spec.circle_radius + 1,
int(drawing_spec.circle_radius * 1.2))
cv2.circle(image, landmark_px, circle_border_radius, WHITE_COLOR,
drawing_spec.thickness)
# Fill color into the circle
cv2.circle(image, landmark_px, drawing_spec.circle_radius,
drawing_spec.color, drawing_spec.thickness)
def draw_axis(
@@ -209,7 +218,7 @@ def draw_axis(
ValueError: If one of the followings:
a) If the input image is not three channel RGB.
"""
if image.shape[2] != RGB_CHANNELS:
if image.shape[2] != _RGB_CHANNELS:
raise ValueError('Input image must contain three channel rgb data.')
image_rows, image_cols, _ = image.shape
# Create axis points in camera coordinate frame.
@@ -231,8 +240,7 @@ def draw_axis(
x_axis = (x_im[1], y_im[1])
y_axis = (x_im[2], y_im[2])
z_axis = (x_im[3], y_im[3])
cv2.arrowedLine(image, origin, x_axis, RED_COLOR,
axis_drawing_spec.thickness)
cv2.arrowedLine(image, origin, x_axis, RED_COLOR, axis_drawing_spec.thickness)
cv2.arrowedLine(image, origin, y_axis, GREEN_COLOR,
axis_drawing_spec.thickness)
cv2.arrowedLine(image, origin, z_axis, BLUE_COLOR,
@@ -274,9 +282,9 @@ def plot_landmarks(landmark_list: landmark_pb2.NormalizedLandmarkList,
plotted_landmarks = {}
for idx, landmark in enumerate(landmark_list.landmark):
if ((landmark.HasField('visibility') and
landmark.visibility < VISIBILITY_THRESHOLD) or
landmark.visibility < _VISIBILITY_THRESHOLD) or
(landmark.HasField('presence') and
landmark.presence < PRESENCE_THRESHOLD)):
landmark.presence < _PRESENCE_THRESHOLD)):
continue
ax.scatter3D(
xs=[-landmark.z],
@@ -29,6 +29,7 @@ DEFAULT_BBOX_DRAWING_SPEC = drawing_utils.DrawingSpec()
DEFAULT_CONNECTION_DRAWING_SPEC = drawing_utils.DrawingSpec()
DEFAULT_CIRCLE_DRAWING_SPEC = drawing_utils.DrawingSpec(color=(0, 0, 255))
DEFAULT_AXIS_DRAWING_SPEC = drawing_utils.DrawingSpec()
DEFAULT_CYCLE_BORDER_COLOR = (224, 224, 224)
class DrawingUtilTest(parameterized.TestCase):
@@ -104,6 +105,10 @@ class DrawingUtilTest(parameterized.TestCase):
landmark_pb2.NormalizedLandmarkList())
image = np.zeros((100, 100, 3), np.uint8)
expected_result = np.copy(image)
cv2.circle(expected_result, (10, 10),
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR,
DEFAULT_CIRCLE_DRAWING_SPEC.thickness)
cv2.circle(expected_result, (10, 10),
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius,
DEFAULT_CIRCLE_DRAWING_SPEC.color,
@@ -127,6 +132,14 @@ class DrawingUtilTest(parameterized.TestCase):
cv2.line(expected_result, start_point, end_point,
DEFAULT_CONNECTION_DRAWING_SPEC.color,
DEFAULT_CONNECTION_DRAWING_SPEC.thickness)
cv2.circle(expected_result, start_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR,
DEFAULT_CIRCLE_DRAWING_SPEC.thickness)
cv2.circle(expected_result, end_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR,
DEFAULT_CIRCLE_DRAWING_SPEC.thickness)
cv2.circle(expected_result, start_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius,
DEFAULT_CIRCLE_DRAWING_SPEC.color,
@@ -187,6 +200,14 @@ class DrawingUtilTest(parameterized.TestCase):
cv2.line(expected_result, start_point, end_point,
DEFAULT_CONNECTION_DRAWING_SPEC.color,
DEFAULT_CONNECTION_DRAWING_SPEC.thickness)
cv2.circle(expected_result, start_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR,
DEFAULT_CIRCLE_DRAWING_SPEC.thickness)
cv2.circle(expected_result, end_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR,
DEFAULT_CIRCLE_DRAWING_SPEC.thickness)
cv2.circle(expected_result, start_point,
DEFAULT_CIRCLE_DRAWING_SPEC.circle_radius,
DEFAULT_CIRCLE_DRAWING_SPEC.color,
@@ -213,6 +234,12 @@ class DrawingUtilTest(parameterized.TestCase):
end_point = (80, 80)
cv2.line(expected_result, start_point, end_point,
connection_drawing_spec.color, connection_drawing_spec.thickness)
cv2.circle(expected_result, start_point,
landmark_drawing_spec.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR, landmark_drawing_spec.thickness)
cv2.circle(expected_result, end_point,
landmark_drawing_spec.circle_radius + 1,
DEFAULT_CYCLE_BORDER_COLOR, landmark_drawing_spec.thickness)
cv2.circle(expected_result, start_point,
landmark_drawing_spec.circle_radius, landmark_drawing_spec.color,
landmark_drawing_spec.thickness)
+12 -133
View File
@@ -36,140 +36,19 @@ from mediapipe.calculators.util import rect_transformation_calculator_pb2
from mediapipe.calculators.util import thresholding_calculator_pb2
# pylint: enable=unused-import
from mediapipe.python.solution_base import SolutionBase
# pylint: disable=unused-import
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_CONTOURS
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_FACE_OVAL
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_LEFT_EYE
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_LEFT_EYEBROW
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_LIPS
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_RIGHT_EYE
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_RIGHT_EYEBROW
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_TESSELATION
# pylint: enable=unused-import
BINARYPB_FILE_PATH = 'mediapipe/modules/face_landmark/face_landmark_front_cpu.binarypb'
FACE_CONNECTIONS = frozenset([
# Lips.
(61, 146),
(146, 91),
(91, 181),
(181, 84),
(84, 17),
(17, 314),
(314, 405),
(405, 321),
(321, 375),
(375, 291),
(61, 185),
(185, 40),
(40, 39),
(39, 37),
(37, 0),
(0, 267),
(267, 269),
(269, 270),
(270, 409),
(409, 291),
(78, 95),
(95, 88),
(88, 178),
(178, 87),
(87, 14),
(14, 317),
(317, 402),
(402, 318),
(318, 324),
(324, 308),
(78, 191),
(191, 80),
(80, 81),
(81, 82),
(82, 13),
(13, 312),
(312, 311),
(311, 310),
(310, 415),
(415, 308),
# Left eye.
(263, 249),
(249, 390),
(390, 373),
(373, 374),
(374, 380),
(380, 381),
(381, 382),
(382, 362),
(263, 466),
(466, 388),
(388, 387),
(387, 386),
(386, 385),
(385, 384),
(384, 398),
(398, 362),
# Left eyebrow.
(276, 283),
(283, 282),
(282, 295),
(295, 285),
(300, 293),
(293, 334),
(334, 296),
(296, 336),
# Right eye.
(33, 7),
(7, 163),
(163, 144),
(144, 145),
(145, 153),
(153, 154),
(154, 155),
(155, 133),
(33, 246),
(246, 161),
(161, 160),
(160, 159),
(159, 158),
(158, 157),
(157, 173),
(173, 133),
# Right eyebrow.
(46, 53),
(53, 52),
(52, 65),
(65, 55),
(70, 63),
(63, 105),
(105, 66),
(66, 107),
# Face oval.
(10, 338),
(338, 297),
(297, 332),
(332, 284),
(284, 251),
(251, 389),
(389, 356),
(356, 454),
(454, 323),
(323, 361),
(361, 288),
(288, 397),
(397, 365),
(365, 379),
(379, 378),
(378, 400),
(400, 377),
(377, 152),
(152, 148),
(148, 176),
(176, 149),
(149, 150),
(150, 136),
(136, 172),
(172, 58),
(58, 132),
(132, 93),
(93, 234),
(234, 127),
(127, 162),
(162, 21),
(21, 54),
(54, 103),
(103, 67),
(67, 109),
(109, 10)
])
class FaceMesh(SolutionBase):
@@ -213,7 +92,7 @@ class FaceMesh(SolutionBase):
.ConstantSidePacketCalculatorOptions.ConstantSidePacket(
bool_value=not static_image_mode)
],
'facedetectionshortrangecpu__TensorsToDetectionsCalculator.min_score_thresh':
'facedetectionshortrangecpu__facedetectionshortrangecommon__TensorsToDetectionsCalculator.min_score_thresh':
min_detection_confidence,
'facelandmarkcpu__ThresholdingCalculator.threshold':
min_tracking_confidence,
@@ -0,0 +1,485 @@
# Copyright 2021 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""MediaPipe FaceMesh connections."""
FACEMESH_LIPS = frozenset([(61, 146), (146, 91), (91, 181), (181, 84), (84, 17),
(17, 314), (314, 405), (405, 321), (321, 375),
(375, 291), (61, 185), (185, 40), (40, 39), (39, 37),
(37, 0), (0, 267),
(267, 269), (269, 270), (270, 409), (409, 291),
(78, 95), (95, 88), (88, 178), (178, 87), (87, 14),
(14, 317), (317, 402), (402, 318), (318, 324),
(324, 308), (78, 191), (191, 80), (80, 81), (81, 82),
(82, 13), (13, 312), (312, 311), (311, 310),
(310, 415), (415, 308)])
FACEMESH_LEFT_EYE = frozenset([(263, 249), (249, 390), (390, 373), (373, 374),
(374, 380), (380, 381), (381, 382), (382, 362),
(263, 466), (466, 388), (388, 387), (387, 386),
(386, 385), (385, 384), (384, 398), (398, 362)])
FACEMESH_LEFT_EYEBROW = frozenset([(276, 283), (283, 282), (282, 295),
(295, 285), (300, 293), (293, 334),
(334, 296), (296, 336)])
FACEMESH_RIGHT_EYE = frozenset([(33, 7), (7, 163), (163, 144), (144, 145),
(145, 153), (153, 154), (154, 155), (155, 133),
(33, 246), (246, 161), (161, 160), (160, 159),
(159, 158), (158, 157), (157, 173), (173, 133)])
FACEMESH_RIGHT_EYEBROW = frozenset([(46, 53), (53, 52), (52, 65), (65, 55),
(70, 63), (63, 105), (105, 66), (66, 107)])
FACEMESH_FACE_OVAL = frozenset([(10, 338), (338, 297), (297, 332), (332, 284),
(284, 251), (251, 389), (389, 356), (356, 454),
(454, 323), (323, 361), (361, 288), (288, 397),
(397, 365), (365, 379), (379, 378), (378, 400),
(400, 377), (377, 152), (152, 148), (148, 176),
(176, 149), (149, 150), (150, 136), (136, 172),
(172, 58), (58, 132), (132, 93), (93, 234),
(234, 127), (127, 162), (162, 21), (21, 54),
(54, 103), (103, 67), (67, 109), (109, 10)])
FACEMESH_CONTOURS = frozenset().union(*[
FACEMESH_LIPS, FACEMESH_LEFT_EYE, FACEMESH_LEFT_EYEBROW, FACEMESH_RIGHT_EYE,
FACEMESH_RIGHT_EYEBROW, FACEMESH_FACE_OVAL
])
FACEMESH_TESSELATION = frozenset([
(127, 34), (34, 139), (139, 127), (11, 0), (0, 37), (37, 11),
(232, 231), (231, 120), (120, 232), (72, 37), (37, 39), (39, 72),
(128, 121), (121, 47), (47, 128), (232, 121), (121, 128), (128, 232),
(104, 69), (69, 67), (67, 104), (175, 171), (171, 148), (148, 175),
(118, 50), (50, 101), (101, 118), (73, 39), (39, 40), (40, 73),
(9, 151), (151, 108), (108, 9), (48, 115), (115, 131), (131, 48),
(194, 204), (204, 211), (211, 194), (74, 40), (40, 185), (185, 74),
(80, 42), (42, 183), (183, 80), (40, 92), (92, 186), (186, 40),
(230, 229), (229, 118), (118, 230), (202, 212), (212, 214), (214, 202),
(83, 18), (18, 17), (17, 83), (76, 61), (61, 146), (146, 76),
(160, 29), (29, 30), (30, 160), (56, 157), (157, 173), (173, 56),
(106, 204), (204, 194), (194, 106), (135, 214), (214, 192), (192, 135),
(203, 165), (165, 98), (98, 203), (21, 71), (71, 68), (68, 21),
(51, 45), (45, 4), (4, 51), (144, 24), (24, 23), (23, 144),
(77, 146), (146, 91), (91, 77), (205, 50), (50, 187), (187, 205),
(201, 200), (200, 18), (18, 201), (91, 106), (106, 182), (182, 91),
(90, 91), (91, 181), (181, 90), (85, 84), (84, 17), (17, 85),
(206, 203), (203, 36), (36, 206), (148, 171), (171, 140), (140, 148),
(92, 40), (40, 39), (39, 92), (193, 189), (189, 244), (244, 193),
(159, 158), (158, 28), (28, 159), (247, 246), (246, 161), (161, 247),
(236, 3), (3, 196), (196, 236), (54, 68), (68, 104), (104, 54),
(193, 168), (168, 8), (8, 193), (117, 228), (228, 31), (31, 117),
(189, 193), (193, 55), (55, 189), (98, 97), (97, 99), (99, 98),
(126, 47), (47, 100), (100, 126), (166, 79), (79, 218), (218, 166),
(155, 154), (154, 26), (26, 155), (209, 49), (49, 131), (131, 209),
(135, 136), (136, 150), (150, 135), (47, 126), (126, 217), (217, 47),
(223, 52), (52, 53), (53, 223), (45, 51), (51, 134), (134, 45),
(211, 170), (170, 140), (140, 211), (67, 69), (69, 108), (108, 67),
(43, 106), (106, 91), (91, 43), (230, 119), (119, 120), (120, 230),
(226, 130), (130, 247), (247, 226), (63, 53), (53, 52), (52, 63),
(238, 20), (20, 242), (242, 238), (46, 70), (70, 156), (156, 46),
(78, 62), (62, 96), (96, 78), (46, 53), (53, 63), (63, 46),
(143, 34), (34, 227), (227, 143), (123, 117), (117, 111), (111, 123),
(44, 125), (125, 19), (19, 44), (236, 134), (134, 51), (51, 236),
(216, 206), (206, 205), (205, 216), (154, 153), (153, 22), (22, 154),
(39, 37), (37, 167), (167, 39), (200, 201), (201, 208), (208, 200),
(36, 142), (142, 100), (100, 36), (57, 212), (212, 202), (202, 57),
(20, 60), (60, 99), (99, 20), (28, 158), (158, 157), (157, 28),
(35, 226), (226, 113), (113, 35), (160, 159), (159, 27), (27, 160),
(204, 202), (202, 210), (210, 204), (113, 225), (225, 46), (46, 113),
(43, 202), (202, 204), (204, 43), (62, 76), (76, 77), (77, 62),
(137, 123), (123, 116), (116, 137), (41, 38), (38, 72), (72, 41),
(203, 129), (129, 142), (142, 203), (64, 98), (98, 240), (240, 64),
(49, 102), (102, 64), (64, 49), (41, 73), (73, 74), (74, 41),
(212, 216), (216, 207), (207, 212), (42, 74), (74, 184), (184, 42),
(169, 170), (170, 211), (211, 169), (170, 149), (149, 176), (176, 170),
(105, 66), (66, 69), (69, 105), (122, 6), (6, 168), (168, 122),
(123, 147), (147, 187), (187, 123), (96, 77), (77, 90), (90, 96),
(65, 55), (55, 107), (107, 65), (89, 90), (90, 180), (180, 89),
(101, 100), (100, 120), (120, 101), (63, 105), (105, 104), (104, 63),
(93, 137), (137, 227), (227, 93), (15, 86), (86, 85), (85, 15),
(129, 102), (102, 49), (49, 129), (14, 87), (87, 86), (86, 14),
(55, 8), (8, 9), (9, 55), (100, 47), (47, 121), (121, 100),
(145, 23), (23, 22), (22, 145), (88, 89), (89, 179), (179, 88),
(6, 122), (122, 196), (196, 6), (88, 95), (95, 96), (96, 88),
(138, 172), (172, 136), (136, 138), (215, 58), (58, 172), (172, 215),
(115, 48), (48, 219), (219, 115), (42, 80), (80, 81), (81, 42),
(195, 3), (3, 51), (51, 195), (43, 146), (146, 61), (61, 43),
(171, 175), (175, 199), (199, 171), (81, 82), (82, 38), (38, 81),
(53, 46), (46, 225), (225, 53), (144, 163), (163, 110), (110, 144),
(52, 65), (65, 66), (66, 52), (229, 228), (228, 117), (117, 229),
(34, 127), (127, 234), (234, 34), (107, 108), (108, 69), (69, 107),
(109, 108), (108, 151), (151, 109), (48, 64), (64, 235), (235, 48),
(62, 78), (78, 191), (191, 62), (129, 209), (209, 126), (126, 129),
(111, 35), (35, 143), (143, 111), (117, 123), (123, 50), (50, 117),
(222, 65), (65, 52), (52, 222), (19, 125), (125, 141), (141, 19),
(221, 55), (55, 65), (65, 221), (3, 195), (195, 197), (197, 3),
(25, 7), (7, 33), (33, 25), (220, 237), (237, 44), (44, 220),
(70, 71), (71, 139), (139, 70), (122, 193), (193, 245), (245, 122),
(247, 130), (130, 33), (33, 247), (71, 21), (21, 162), (162, 71),
(170, 169), (169, 150), (150, 170), (188, 174), (174, 196), (196, 188),
(216, 186), (186, 92), (92, 216), (2, 97), (97, 167), (167, 2),
(141, 125), (125, 241), (241, 141), (164, 167), (167, 37), (37, 164),
(72, 38), (38, 12), (12, 72), (38, 82), (82, 13), (13, 38),
(63, 68), (68, 71), (71, 63), (226, 35), (35, 111), (111, 226),
(101, 50), (50, 205), (205, 101), (206, 92), (92, 165), (165, 206),
(209, 198), (198, 217), (217, 209), (165, 167), (167, 97), (97, 165),
(220, 115), (115, 218), (218, 220), (133, 112), (112, 243), (243, 133),
(239, 238), (238, 241), (241, 239), (214, 135), (135, 169), (169, 214),
(190, 173), (173, 133), (133, 190), (171, 208), (208, 32), (32, 171),
(125, 44), (44, 237), (237, 125), (86, 87), (87, 178), (178, 86),
(85, 86), (86, 179), (179, 85), (84, 85), (85, 180), (180, 84),
(83, 84), (84, 181), (181, 83), (201, 83), (83, 182), (182, 201),
(137, 93), (93, 132), (132, 137), (76, 62), (62, 183), (183, 76),
(61, 76), (76, 184), (184, 61), (57, 61), (61, 185), (185, 57),
(212, 57), (57, 186), (186, 212), (214, 207), (207, 187), (187, 214),
(34, 143), (143, 156), (156, 34), (79, 239), (239, 237), (237, 79),
(123, 137), (137, 177), (177, 123), (44, 1), (1, 4), (4, 44),
(201, 194), (194, 32), (32, 201), (64, 102), (102, 129), (129, 64),
(213, 215), (215, 138), (138, 213), (59, 166), (166, 219), (219, 59),
(242, 99), (99, 97), (97, 242), (2, 94), (94, 141), (141, 2),
(75, 59), (59, 235), (235, 75), (24, 110), (110, 228), (228, 24),
(25, 130), (130, 226), (226, 25), (23, 24), (24, 229), (229, 23),
(22, 23), (23, 230), (230, 22), (26, 22), (22, 231), (231, 26),
(112, 26), (26, 232), (232, 112), (189, 190), (190, 243), (243, 189),
(221, 56), (56, 190), (190, 221), (28, 56), (56, 221), (221, 28),
(27, 28), (28, 222), (222, 27), (29, 27), (27, 223), (223, 29),
(30, 29), (29, 224), (224, 30), (247, 30), (30, 225), (225, 247),
(238, 79), (79, 20), (20, 238), (166, 59), (59, 75), (75, 166),
(60, 75), (75, 240), (240, 60), (147, 177), (177, 215), (215, 147),
(20, 79), (79, 166), (166, 20), (187, 147), (147, 213), (213, 187),
(112, 233), (233, 244), (244, 112), (233, 128), (128, 245), (245, 233),
(128, 114), (114, 188), (188, 128), (114, 217), (217, 174), (174, 114),
(131, 115), (115, 220), (220, 131), (217, 198), (198, 236), (236, 217),
(198, 131), (131, 134), (134, 198), (177, 132), (132, 58), (58, 177),
(143, 35), (35, 124), (124, 143), (110, 163), (163, 7), (7, 110),
(228, 110), (110, 25), (25, 228), (356, 389), (389, 368), (368, 356),
(11, 302), (302, 267), (267, 11), (452, 350), (350, 349), (349, 452),
(302, 303), (303, 269), (269, 302), (357, 343), (343, 277), (277, 357),
(452, 453), (453, 357), (357, 452), (333, 332), (332, 297), (297, 333),
(175, 152), (152, 377), (377, 175), (347, 348), (348, 330), (330, 347),
(303, 304), (304, 270), (270, 303), (9, 336), (336, 337), (337, 9),
(278, 279), (279, 360), (360, 278), (418, 262), (262, 431), (431, 418),
(304, 408), (408, 409), (409, 304), (310, 415), (415, 407), (407, 310),
(270, 409), (409, 410), (410, 270), (450, 348), (348, 347), (347, 450),
(422, 430), (430, 434), (434, 422), (313, 314), (314, 17), (17, 313),
(306, 307), (307, 375), (375, 306), (387, 388), (388, 260), (260, 387),
(286, 414), (414, 398), (398, 286), (335, 406), (406, 418), (418, 335),
(364, 367), (367, 416), (416, 364), (423, 358), (358, 327), (327, 423),
(251, 284), (284, 298), (298, 251), (281, 5), (5, 4), (4, 281),
(373, 374), (374, 253), (253, 373), (307, 320), (320, 321), (321, 307),
(425, 427), (427, 411), (411, 425), (421, 313), (313, 18), (18, 421),
(321, 405), (405, 406), (406, 321), (320, 404), (404, 405), (405, 320),
(315, 16), (16, 17), (17, 315), (426, 425), (425, 266), (266, 426),
(377, 400), (400, 369), (369, 377), (322, 391), (391, 269), (269, 322),
(417, 465), (465, 464), (464, 417), (386, 257), (257, 258), (258, 386),
(466, 260), (260, 388), (388, 466), (456, 399), (399, 419), (419, 456),
(284, 332), (332, 333), (333, 284), (417, 285), (285, 8), (8, 417),
(346, 340), (340, 261), (261, 346), (413, 441), (441, 285), (285, 413),
(327, 460), (460, 328), (328, 327), (355, 371), (371, 329), (329, 355),
(392, 439), (439, 438), (438, 392), (382, 341), (341, 256), (256, 382),
(429, 420), (420, 360), (360, 429), (364, 394), (394, 379), (379, 364),
(277, 343), (343, 437), (437, 277), (443, 444), (444, 283), (283, 443),
(275, 440), (440, 363), (363, 275), (431, 262), (262, 369), (369, 431),
(297, 338), (338, 337), (337, 297), (273, 375), (375, 321), (321, 273),
(450, 451), (451, 349), (349, 450), (446, 342), (342, 467), (467, 446),
(293, 334), (334, 282), (282, 293), (458, 461), (461, 462), (462, 458),
(276, 353), (353, 383), (383, 276), (308, 324), (324, 325), (325, 308),
(276, 300), (300, 293), (293, 276), (372, 345), (345, 447), (447, 372),
(352, 345), (345, 340), (340, 352), (274, 1), (1, 19), (19, 274),
(456, 248), (248, 281), (281, 456), (436, 427), (427, 425), (425, 436),
(381, 256), (256, 252), (252, 381), (269, 391), (391, 393), (393, 269),
(200, 199), (199, 428), (428, 200), (266, 330), (330, 329), (329, 266),
(287, 273), (273, 422), (422, 287), (250, 462), (462, 328), (328, 250),
(258, 286), (286, 384), (384, 258), (265, 353), (353, 342), (342, 265),
(387, 259), (259, 257), (257, 387), (424, 431), (431, 430), (430, 424),
(342, 353), (353, 276), (276, 342), (273, 335), (335, 424), (424, 273),
(292, 325), (325, 307), (307, 292), (366, 447), (447, 345), (345, 366),
(271, 303), (303, 302), (302, 271), (423, 266), (266, 371), (371, 423),
(294, 455), (455, 460), (460, 294), (279, 278), (278, 294), (294, 279),
(271, 272), (272, 304), (304, 271), (432, 434), (434, 427), (427, 432),
(272, 407), (407, 408), (408, 272), (394, 430), (430, 431), (431, 394),
(395, 369), (369, 400), (400, 395), (334, 333), (333, 299), (299, 334),
(351, 417), (417, 168), (168, 351), (352, 280), (280, 411), (411, 352),
(325, 319), (319, 320), (320, 325), (295, 296), (296, 336), (336, 295),
(319, 403), (403, 404), (404, 319), (330, 348), (348, 349), (349, 330),
(293, 298), (298, 333), (333, 293), (323, 454), (454, 447), (447, 323),
(15, 16), (16, 315), (315, 15), (358, 429), (429, 279), (279, 358),
(14, 15), (15, 316), (316, 14), (285, 336), (336, 9), (9, 285),
(329, 349), (349, 350), (350, 329), (374, 380), (380, 252), (252, 374),
(318, 402), (402, 403), (403, 318), (6, 197), (197, 419), (419, 6),
(318, 319), (319, 325), (325, 318), (367, 364), (364, 365), (365, 367),
(435, 367), (367, 397), (397, 435), (344, 438), (438, 439), (439, 344),
(272, 271), (271, 311), (311, 272), (195, 5), (5, 281), (281, 195),
(273, 287), (287, 291), (291, 273), (396, 428), (428, 199), (199, 396),
(311, 271), (271, 268), (268, 311), (283, 444), (444, 445), (445, 283),
(373, 254), (254, 339), (339, 373), (282, 334), (334, 296), (296, 282),
(449, 347), (347, 346), (346, 449), (264, 447), (447, 454), (454, 264),
(336, 296), (296, 299), (299, 336), (338, 10), (10, 151), (151, 338),
(278, 439), (439, 455), (455, 278), (292, 407), (407, 415), (415, 292),
(358, 371), (371, 355), (355, 358), (340, 345), (345, 372), (372, 340),
(346, 347), (347, 280), (280, 346), (442, 443), (443, 282), (282, 442),
(19, 94), (94, 370), (370, 19), (441, 442), (442, 295), (295, 441),
(248, 419), (419, 197), (197, 248), (263, 255), (255, 359), (359, 263),
(440, 275), (275, 274), (274, 440), (300, 383), (383, 368), (368, 300),
(351, 412), (412, 465), (465, 351), (263, 467), (467, 466), (466, 263),
(301, 368), (368, 389), (389, 301), (395, 378), (378, 379), (379, 395),
(412, 351), (351, 419), (419, 412), (436, 426), (426, 322), (322, 436),
(2, 164), (164, 393), (393, 2), (370, 462), (462, 461), (461, 370),
(164, 0), (0, 267), (267, 164), (302, 11), (11, 12), (12, 302),
(268, 12), (12, 13), (13, 268), (293, 300), (300, 301), (301, 293),
(446, 261), (261, 340), (340, 446), (330, 266), (266, 425), (425, 330),
(426, 423), (423, 391), (391, 426), (429, 355), (355, 437), (437, 429),
(391, 327), (327, 326), (326, 391), (440, 457), (457, 438), (438, 440),
(341, 382), (382, 362), (362, 341), (459, 457), (457, 461), (461, 459),
(434, 430), (430, 394), (394, 434), (414, 463), (463, 362), (362, 414),
(396, 369), (369, 262), (262, 396), (354, 461), (461, 457), (457, 354),
(316, 403), (403, 402), (402, 316), (315, 404), (404, 403), (403, 315),
(314, 405), (405, 404), (404, 314), (313, 406), (406, 405), (405, 313),
(421, 418), (418, 406), (406, 421), (366, 401), (401, 361), (361, 366),
(306, 408), (408, 407), (407, 306), (291, 409), (409, 408), (408, 291),
(287, 410), (410, 409), (409, 287), (432, 436), (436, 410), (410, 432),
(434, 416), (416, 411), (411, 434), (264, 368), (368, 383), (383, 264),
(309, 438), (438, 457), (457, 309), (352, 376), (376, 401), (401, 352),
(274, 275), (275, 4), (4, 274), (421, 428), (428, 262), (262, 421),
(294, 327), (327, 358), (358, 294), (433, 416), (416, 367), (367, 433),
(289, 455), (455, 439), (439, 289), (462, 370), (370, 326), (326, 462),
(2, 326), (326, 370), (370, 2), (305, 460), (460, 455), (455, 305),
(254, 449), (449, 448), (448, 254), (255, 261), (261, 446), (446, 255),
(253, 450), (450, 449), (449, 253), (252, 451), (451, 450), (450, 252),
(256, 452), (452, 451), (451, 256), (341, 453), (453, 452), (452, 341),
(413, 464), (464, 463), (463, 413), (441, 413), (413, 414), (414, 441),
(258, 442), (442, 441), (441, 258), (257, 443), (443, 442), (442, 257),
(259, 444), (444, 443), (443, 259), (260, 445), (445, 444), (444, 260),
(467, 342), (342, 445), (445, 467), (459, 458), (458, 250), (250, 459),
(289, 392), (392, 290), (290, 289), (290, 328), (328, 460), (460, 290),
(376, 433), (433, 435), (435, 376), (250, 290), (290, 392), (392, 250),
(411, 416), (416, 433), (433, 411), (341, 463), (463, 464), (464, 341),
(453, 464), (464, 465), (465, 453), (357, 465), (465, 412), (412, 357),
(343, 412), (412, 399), (399, 343), (360, 363), (363, 440), (440, 360),
(437, 399), (399, 456), (456, 437), (420, 456), (456, 363), (363, 420),
(401, 435), (435, 288), (288, 401), (372, 383), (383, 353), (353, 372),
(339, 255), (255, 249), (249, 339), (448, 261), (261, 255), (255, 448),
(133, 243), (243, 190), (190, 133), (133, 155), (155, 112), (112, 133),
(33, 246), (246, 247), (247, 33), (33, 130), (130, 25), (25, 33),
(398, 384), (384, 286), (286, 398), (362, 398), (398, 414), (414, 362),
(362, 463), (463, 341), (341, 362), (263, 359), (359, 467), (467, 263),
(263, 249), (249, 255), (255, 263), (466, 467), (467, 260), (260, 466),
(75, 60), (60, 166), (166, 75), (238, 239), (239, 79), (79, 238),
(162, 127), (127, 139), (139, 162), (72, 11), (11, 37), (37, 72),
(121, 232), (232, 120), (120, 121), (73, 72), (72, 39), (39, 73),
(114, 128), (128, 47), (47, 114), (233, 232), (232, 128), (128, 233),
(103, 104), (104, 67), (67, 103), (152, 175), (175, 148), (148, 152),
(119, 118), (118, 101), (101, 119), (74, 73), (73, 40), (40, 74),
(107, 9), (9, 108), (108, 107), (49, 48), (48, 131), (131, 49),
(32, 194), (194, 211), (211, 32), (184, 74), (74, 185), (185, 184),
(191, 80), (80, 183), (183, 191), (185, 40), (40, 186), (186, 185),
(119, 230), (230, 118), (118, 119), (210, 202), (202, 214), (214, 210),
(84, 83), (83, 17), (17, 84), (77, 76), (76, 146), (146, 77),
(161, 160), (160, 30), (30, 161), (190, 56), (56, 173), (173, 190),
(182, 106), (106, 194), (194, 182), (138, 135), (135, 192), (192, 138),
(129, 203), (203, 98), (98, 129), (54, 21), (21, 68), (68, 54),
(5, 51), (51, 4), (4, 5), (145, 144), (144, 23), (23, 145),
(90, 77), (77, 91), (91, 90), (207, 205), (205, 187), (187, 207),
(83, 201), (201, 18), (18, 83), (181, 91), (91, 182), (182, 181),
(180, 90), (90, 181), (181, 180), (16, 85), (85, 17), (17, 16),
(205, 206), (206, 36), (36, 205), (176, 148), (148, 140), (140, 176),
(165, 92), (92, 39), (39, 165), (245, 193), (193, 244), (244, 245),
(27, 159), (159, 28), (28, 27), (30, 247), (247, 161), (161, 30),
(174, 236), (236, 196), (196, 174), (103, 54), (54, 104), (104, 103),
(55, 193), (193, 8), (8, 55), (111, 117), (117, 31), (31, 111),
(221, 189), (189, 55), (55, 221), (240, 98), (98, 99), (99, 240),
(142, 126), (126, 100), (100, 142), (219, 166), (166, 218), (218, 219),
(112, 155), (155, 26), (26, 112), (198, 209), (209, 131), (131, 198),
(169, 135), (135, 150), (150, 169), (114, 47), (47, 217), (217, 114),
(224, 223), (223, 53), (53, 224), (220, 45), (45, 134), (134, 220),
(32, 211), (211, 140), (140, 32), (109, 67), (67, 108), (108, 109),
(146, 43), (43, 91), (91, 146), (231, 230), (230, 120), (120, 231),
(113, 226), (226, 247), (247, 113), (105, 63), (63, 52), (52, 105),
(241, 238), (238, 242), (242, 241), (124, 46), (46, 156), (156, 124),
(95, 78), (78, 96), (96, 95), (70, 46), (46, 63), (63, 70),
(116, 143), (143, 227), (227, 116), (116, 123), (123, 111), (111, 116),
(1, 44), (44, 19), (19, 1), (3, 236), (236, 51), (51, 3),
(207, 216), (216, 205), (205, 207), (26, 154), (154, 22), (22, 26),
(165, 39), (39, 167), (167, 165), (199, 200), (200, 208), (208, 199),
(101, 36), (36, 100), (100, 101), (43, 57), (57, 202), (202, 43),
(242, 20), (20, 99), (99, 242), (56, 28), (28, 157), (157, 56),
(124, 35), (35, 113), (113, 124), (29, 160), (160, 27), (27, 29),
(211, 204), (204, 210), (210, 211), (124, 113), (113, 46), (46, 124),
(106, 43), (43, 204), (204, 106), (96, 62), (62, 77), (77, 96),
(227, 137), (137, 116), (116, 227), (73, 41), (41, 72), (72, 73),
(36, 203), (203, 142), (142, 36), (235, 64), (64, 240), (240, 235),
(48, 49), (49, 64), (64, 48), (42, 41), (41, 74), (74, 42),
(214, 212), (212, 207), (207, 214), (183, 42), (42, 184), (184, 183),
(210, 169), (169, 211), (211, 210), (140, 170), (170, 176), (176, 140),
(104, 105), (105, 69), (69, 104), (193, 122), (122, 168), (168, 193),
(50, 123), (123, 187), (187, 50), (89, 96), (96, 90), (90, 89),
(66, 65), (65, 107), (107, 66), (179, 89), (89, 180), (180, 179),
(119, 101), (101, 120), (120, 119), (68, 63), (63, 104), (104, 68),
(234, 93), (93, 227), (227, 234), (16, 15), (15, 85), (85, 16),
(209, 129), (129, 49), (49, 209), (15, 14), (14, 86), (86, 15),
(107, 55), (55, 9), (9, 107), (120, 100), (100, 121), (121, 120),
(153, 145), (145, 22), (22, 153), (178, 88), (88, 179), (179, 178),
(197, 6), (6, 196), (196, 197), (89, 88), (88, 96), (96, 89),
(135, 138), (138, 136), (136, 135), (138, 215), (215, 172), (172, 138),
(218, 115), (115, 219), (219, 218), (41, 42), (42, 81), (81, 41),
(5, 195), (195, 51), (51, 5), (57, 43), (43, 61), (61, 57),
(208, 171), (171, 199), (199, 208), (41, 81), (81, 38), (38, 41),
(224, 53), (53, 225), (225, 224), (24, 144), (144, 110), (110, 24),
(105, 52), (52, 66), (66, 105), (118, 229), (229, 117), (117, 118),
(227, 34), (34, 234), (234, 227), (66, 107), (107, 69), (69, 66),
(10, 109), (109, 151), (151, 10), (219, 48), (48, 235), (235, 219),
(183, 62), (62, 191), (191, 183), (142, 129), (129, 126), (126, 142),
(116, 111), (111, 143), (143, 116), (118, 117), (117, 50), (50, 118),
(223, 222), (222, 52), (52, 223), (94, 19), (19, 141), (141, 94),
(222, 221), (221, 65), (65, 222), (196, 3), (3, 197), (197, 196),
(45, 220), (220, 44), (44, 45), (156, 70), (70, 139), (139, 156),
(188, 122), (122, 245), (245, 188), (139, 71), (71, 162), (162, 139),
(149, 170), (170, 150), (150, 149), (122, 188), (188, 196), (196, 122),
(206, 216), (216, 92), (92, 206), (164, 2), (2, 167), (167, 164),
(242, 141), (141, 241), (241, 242), (0, 164), (164, 37), (37, 0),
(11, 72), (72, 12), (12, 11), (12, 38), (38, 13), (13, 12),
(70, 63), (63, 71), (71, 70), (31, 226), (226, 111), (111, 31),
(36, 101), (101, 205), (205, 36), (203, 206), (206, 165), (165, 203),
(126, 209), (209, 217), (217, 126), (98, 165), (165, 97), (97, 98),
(237, 220), (220, 218), (218, 237), (237, 239), (239, 241), (241, 237),
(210, 214), (214, 169), (169, 210), (140, 171), (171, 32), (32, 140),
(241, 125), (125, 237), (237, 241), (179, 86), (86, 178), (178, 179),
(180, 85), (85, 179), (179, 180), (181, 84), (84, 180), (180, 181),
(182, 83), (83, 181), (181, 182), (194, 201), (201, 182), (182, 194),
(177, 137), (137, 132), (132, 177), (184, 76), (76, 183), (183, 184),
(185, 61), (61, 184), (184, 185), (186, 57), (57, 185), (185, 186),
(216, 212), (212, 186), (186, 216), (192, 214), (214, 187), (187, 192),
(139, 34), (34, 156), (156, 139), (218, 79), (79, 237), (237, 218),
(147, 123), (123, 177), (177, 147), (45, 44), (44, 4), (4, 45),
(208, 201), (201, 32), (32, 208), (98, 64), (64, 129), (129, 98),
(192, 213), (213, 138), (138, 192), (235, 59), (59, 219), (219, 235),
(141, 242), (242, 97), (97, 141), (97, 2), (2, 141), (141, 97),
(240, 75), (75, 235), (235, 240), (229, 24), (24, 228), (228, 229),
(31, 25), (25, 226), (226, 31), (230, 23), (23, 229), (229, 230),
(231, 22), (22, 230), (230, 231), (232, 26), (26, 231), (231, 232),
(233, 112), (112, 232), (232, 233), (244, 189), (189, 243), (243, 244),
(189, 221), (221, 190), (190, 189), (222, 28), (28, 221), (221, 222),
(223, 27), (27, 222), (222, 223), (224, 29), (29, 223), (223, 224),
(225, 30), (30, 224), (224, 225), (113, 247), (247, 225), (225, 113),
(99, 60), (60, 240), (240, 99), (213, 147), (147, 215), (215, 213),
(60, 20), (20, 166), (166, 60), (192, 187), (187, 213), (213, 192),
(243, 112), (112, 244), (244, 243), (244, 233), (233, 245), (245, 244),
(245, 128), (128, 188), (188, 245), (188, 114), (114, 174), (174, 188),
(134, 131), (131, 220), (220, 134), (174, 217), (217, 236), (236, 174),
(236, 198), (198, 134), (134, 236), (215, 177), (177, 58), (58, 215),
(156, 143), (143, 124), (124, 156), (25, 110), (110, 7), (7, 25),
(31, 228), (228, 25), (25, 31), (264, 356), (356, 368), (368, 264),
(0, 11), (11, 267), (267, 0), (451, 452), (452, 349), (349, 451),
(267, 302), (302, 269), (269, 267), (350, 357), (357, 277), (277, 350),
(350, 452), (452, 357), (357, 350), (299, 333), (333, 297), (297, 299),
(396, 175), (175, 377), (377, 396), (280, 347), (347, 330), (330, 280),
(269, 303), (303, 270), (270, 269), (151, 9), (9, 337), (337, 151),
(344, 278), (278, 360), (360, 344), (424, 418), (418, 431), (431, 424),
(270, 304), (304, 409), (409, 270), (272, 310), (310, 407), (407, 272),
(322, 270), (270, 410), (410, 322), (449, 450), (450, 347), (347, 449),
(432, 422), (422, 434), (434, 432), (18, 313), (313, 17), (17, 18),
(291, 306), (306, 375), (375, 291), (259, 387), (387, 260), (260, 259),
(424, 335), (335, 418), (418, 424), (434, 364), (364, 416), (416, 434),
(391, 423), (423, 327), (327, 391), (301, 251), (251, 298), (298, 301),
(275, 281), (281, 4), (4, 275), (254, 373), (373, 253), (253, 254),
(375, 307), (307, 321), (321, 375), (280, 425), (425, 411), (411, 280),
(200, 421), (421, 18), (18, 200), (335, 321), (321, 406), (406, 335),
(321, 320), (320, 405), (405, 321), (314, 315), (315, 17), (17, 314),
(423, 426), (426, 266), (266, 423), (396, 377), (377, 369), (369, 396),
(270, 322), (322, 269), (269, 270), (413, 417), (417, 464), (464, 413),
(385, 386), (386, 258), (258, 385), (248, 456), (456, 419), (419, 248),
(298, 284), (284, 333), (333, 298), (168, 417), (417, 8), (8, 168),
(448, 346), (346, 261), (261, 448), (417, 413), (413, 285), (285, 417),
(326, 327), (327, 328), (328, 326), (277, 355), (355, 329), (329, 277),
(309, 392), (392, 438), (438, 309), (381, 382), (382, 256), (256, 381),
(279, 429), (429, 360), (360, 279), (365, 364), (364, 379), (379, 365),
(355, 277), (277, 437), (437, 355), (282, 443), (443, 283), (283, 282),
(281, 275), (275, 363), (363, 281), (395, 431), (431, 369), (369, 395),
(299, 297), (297, 337), (337, 299), (335, 273), (273, 321), (321, 335),
(348, 450), (450, 349), (349, 348), (359, 446), (446, 467), (467, 359),
(283, 293), (293, 282), (282, 283), (250, 458), (458, 462), (462, 250),
(300, 276), (276, 383), (383, 300), (292, 308), (308, 325), (325, 292),
(283, 276), (276, 293), (293, 283), (264, 372), (372, 447), (447, 264),
(346, 352), (352, 340), (340, 346), (354, 274), (274, 19), (19, 354),
(363, 456), (456, 281), (281, 363), (426, 436), (436, 425), (425, 426),
(380, 381), (381, 252), (252, 380), (267, 269), (269, 393), (393, 267),
(421, 200), (200, 428), (428, 421), (371, 266), (266, 329), (329, 371),
(432, 287), (287, 422), (422, 432), (290, 250), (250, 328), (328, 290),
(385, 258), (258, 384), (384, 385), (446, 265), (265, 342), (342, 446),
(386, 387), (387, 257), (257, 386), (422, 424), (424, 430), (430, 422),
(445, 342), (342, 276), (276, 445), (422, 273), (273, 424), (424, 422),
(306, 292), (292, 307), (307, 306), (352, 366), (366, 345), (345, 352),
(268, 271), (271, 302), (302, 268), (358, 423), (423, 371), (371, 358),
(327, 294), (294, 460), (460, 327), (331, 279), (279, 294), (294, 331),
(303, 271), (271, 304), (304, 303), (436, 432), (432, 427), (427, 436),
(304, 272), (272, 408), (408, 304), (395, 394), (394, 431), (431, 395),
(378, 395), (395, 400), (400, 378), (296, 334), (334, 299), (299, 296),
(6, 351), (351, 168), (168, 6), (376, 352), (352, 411), (411, 376),
(307, 325), (325, 320), (320, 307), (285, 295), (295, 336), (336, 285),
(320, 319), (319, 404), (404, 320), (329, 330), (330, 349), (349, 329),
(334, 293), (293, 333), (333, 334), (366, 323), (323, 447), (447, 366),
(316, 15), (15, 315), (315, 316), (331, 358), (358, 279), (279, 331),
(317, 14), (14, 316), (316, 317), (8, 285), (285, 9), (9, 8),
(277, 329), (329, 350), (350, 277), (253, 374), (374, 252), (252, 253),
(319, 318), (318, 403), (403, 319), (351, 6), (6, 419), (419, 351),
(324, 318), (318, 325), (325, 324), (397, 367), (367, 365), (365, 397),
(288, 435), (435, 397), (397, 288), (278, 344), (344, 439), (439, 278),
(310, 272), (272, 311), (311, 310), (248, 195), (195, 281), (281, 248),
(375, 273), (273, 291), (291, 375), (175, 396), (396, 199), (199, 175),
(312, 311), (311, 268), (268, 312), (276, 283), (283, 445), (445, 276),
(390, 373), (373, 339), (339, 390), (295, 282), (282, 296), (296, 295),
(448, 449), (449, 346), (346, 448), (356, 264), (264, 454), (454, 356),
(337, 336), (336, 299), (299, 337), (337, 338), (338, 151), (151, 337),
(294, 278), (278, 455), (455, 294), (308, 292), (292, 415), (415, 308),
(429, 358), (358, 355), (355, 429), (265, 340), (340, 372), (372, 265),
(352, 346), (346, 280), (280, 352), (295, 442), (442, 282), (282, 295),
(354, 19), (19, 370), (370, 354), (285, 441), (441, 295), (295, 285),
(195, 248), (248, 197), (197, 195), (457, 440), (440, 274), (274, 457),
(301, 300), (300, 368), (368, 301), (417, 351), (351, 465), (465, 417),
(251, 301), (301, 389), (389, 251), (394, 395), (395, 379), (379, 394),
(399, 412), (412, 419), (419, 399), (410, 436), (436, 322), (322, 410),
(326, 2), (2, 393), (393, 326), (354, 370), (370, 461), (461, 354),
(393, 164), (164, 267), (267, 393), (268, 302), (302, 12), (12, 268),
(312, 268), (268, 13), (13, 312), (298, 293), (293, 301), (301, 298),
(265, 446), (446, 340), (340, 265), (280, 330), (330, 425), (425, 280),
(322, 426), (426, 391), (391, 322), (420, 429), (429, 437), (437, 420),
(393, 391), (391, 326), (326, 393), (344, 440), (440, 438), (438, 344),
(458, 459), (459, 461), (461, 458), (364, 434), (434, 394), (394, 364),
(428, 396), (396, 262), (262, 428), (274, 354), (354, 457), (457, 274),
(317, 316), (316, 402), (402, 317), (316, 315), (315, 403), (403, 316),
(315, 314), (314, 404), (404, 315), (314, 313), (313, 405), (405, 314),
(313, 421), (421, 406), (406, 313), (323, 366), (366, 361), (361, 323),
(292, 306), (306, 407), (407, 292), (306, 291), (291, 408), (408, 306),
(291, 287), (287, 409), (409, 291), (287, 432), (432, 410), (410, 287),
(427, 434), (434, 411), (411, 427), (372, 264), (264, 383), (383, 372),
(459, 309), (309, 457), (457, 459), (366, 352), (352, 401), (401, 366),
(1, 274), (274, 4), (4, 1), (418, 421), (421, 262), (262, 418),
(331, 294), (294, 358), (358, 331), (435, 433), (433, 367), (367, 435),
(392, 289), (289, 439), (439, 392), (328, 462), (462, 326), (326, 328),
(94, 2), (2, 370), (370, 94), (289, 305), (305, 455), (455, 289),
(339, 254), (254, 448), (448, 339), (359, 255), (255, 446), (446, 359),
(254, 253), (253, 449), (449, 254), (253, 252), (252, 450), (450, 253),
(252, 256), (256, 451), (451, 252), (256, 341), (341, 452), (452, 256),
(414, 413), (413, 463), (463, 414), (286, 441), (441, 414), (414, 286),
(286, 258), (258, 441), (441, 286), (258, 257), (257, 442), (442, 258),
(257, 259), (259, 443), (443, 257), (259, 260), (260, 444), (444, 259),
(260, 467), (467, 445), (445, 260), (309, 459), (459, 250), (250, 309),
(305, 289), (289, 290), (290, 305), (305, 290), (290, 460), (460, 305),
(401, 376), (376, 435), (435, 401), (309, 250), (250, 392), (392, 309),
(376, 411), (411, 433), (433, 376), (453, 341), (341, 464), (464, 453),
(357, 453), (453, 465), (465, 357), (343, 357), (357, 412), (412, 343),
(437, 343), (343, 399), (399, 437), (344, 360), (360, 440), (440, 344),
(420, 437), (437, 456), (456, 420), (360, 420), (420, 363), (363, 360),
(361, 401), (401, 288), (288, 361), (265, 372), (372, 353), (353, 265),
(390, 339), (339, 249), (249, 390), (339, 448), (448, 255), (255, 339)])
+14 -4
View File
@@ -26,6 +26,7 @@ import numpy.testing as npt
# resources dependency
# undeclared dependency
from mediapipe.python.solutions import drawing_styles
from mediapipe.python.solutions import drawing_utils as mp_drawing
from mediapipe.python.solutions import face_mesh as mp_faces
@@ -70,12 +71,21 @@ EYE_INDICES_TO_LANDMARKS = {
class FaceMeshTest(parameterized.TestCase):
def _annotate(self, frame: np.ndarray, results: NamedTuple, idx: int):
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
for face_landmarks in results.multi_face_landmarks:
mp_drawing.draw_landmarks(
image=frame,
landmark_list=face_landmarks,
landmark_drawing_spec=drawing_spec)
frame,
face_landmarks,
mp_faces.FACEMESH_TESSELATION,
landmark_drawing_spec=None,
connection_drawing_spec=drawing_styles
.get_default_face_mesh_tesselation_style())
mp_drawing.draw_landmarks(
frame,
face_landmarks,
mp_faces.FACEMESH_CONTOURS,
landmark_drawing_spec=None,
connection_drawing_spec=drawing_styles
.get_default_face_mesh_contours_style())
path = os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] +
'_frame_{}.png'.format(idx))
cv2.imwrite(path, frame)
+3 -23
View File
@@ -37,6 +37,9 @@ from mediapipe.calculators.util import rect_transformation_calculator_pb2
from mediapipe.calculators.util import thresholding_calculator_pb2
# pylint: enable=unused-import
from mediapipe.python.solution_base import SolutionBase
# pylint: disable=unused-import
from mediapipe.python.solutions.hands_connections import HAND_CONNECTIONS
# pylint: enable=unused-import
class HandLandmark(enum.IntEnum):
@@ -65,29 +68,6 @@ class HandLandmark(enum.IntEnum):
BINARYPB_FILE_PATH = 'mediapipe/modules/hand_landmark/hand_landmark_tracking_cpu.binarypb'
HAND_CONNECTIONS = frozenset([
(HandLandmark.WRIST, HandLandmark.THUMB_CMC),
(HandLandmark.THUMB_CMC, HandLandmark.THUMB_MCP),
(HandLandmark.THUMB_MCP, HandLandmark.THUMB_IP),
(HandLandmark.THUMB_IP, HandLandmark.THUMB_TIP),
(HandLandmark.WRIST, HandLandmark.INDEX_FINGER_MCP),
(HandLandmark.INDEX_FINGER_MCP, HandLandmark.INDEX_FINGER_PIP),
(HandLandmark.INDEX_FINGER_PIP, HandLandmark.INDEX_FINGER_DIP),
(HandLandmark.INDEX_FINGER_DIP, HandLandmark.INDEX_FINGER_TIP),
(HandLandmark.INDEX_FINGER_MCP, HandLandmark.MIDDLE_FINGER_MCP),
(HandLandmark.MIDDLE_FINGER_MCP, HandLandmark.MIDDLE_FINGER_PIP),
(HandLandmark.MIDDLE_FINGER_PIP, HandLandmark.MIDDLE_FINGER_DIP),
(HandLandmark.MIDDLE_FINGER_DIP, HandLandmark.MIDDLE_FINGER_TIP),
(HandLandmark.MIDDLE_FINGER_MCP, HandLandmark.RING_FINGER_MCP),
(HandLandmark.RING_FINGER_MCP, HandLandmark.RING_FINGER_PIP),
(HandLandmark.RING_FINGER_PIP, HandLandmark.RING_FINGER_DIP),
(HandLandmark.RING_FINGER_DIP, HandLandmark.RING_FINGER_TIP),
(HandLandmark.RING_FINGER_MCP, HandLandmark.PINKY_MCP),
(HandLandmark.WRIST, HandLandmark.PINKY_MCP),
(HandLandmark.PINKY_MCP, HandLandmark.PINKY_PIP),
(HandLandmark.PINKY_PIP, HandLandmark.PINKY_DIP),
(HandLandmark.PINKY_DIP, HandLandmark.PINKY_TIP)
])
class Hands(SolutionBase):
@@ -0,0 +1,32 @@
# Copyright 2021 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""MediaPipe Hands connections."""
HAND_PALM_CONNECTIONS = ((0, 1), (0, 5), (9, 13), (13, 17), (5, 9), (0, 17))
HAND_THUMB_CONNECTIONS = ((1, 2), (2, 3), (3, 4))
HAND_INDEX_FINGER_CONNECTIONS = ((5, 6), (6, 7), (7, 8))
HAND_MIDDLE_FINGER_CONNECTIONS = ((9, 10), (10, 11), (11, 12))
HAND_RING_FINGER_CONNECTIONS = ((13, 14), (14, 15), (15, 16))
HAND_PINKY_FINGER_CONNECTIONS = ((17, 18), (18, 19), (19, 20))
HAND_CONNECTIONS = frozenset().union(*[
HAND_PALM_CONNECTIONS, HAND_THUMB_CONNECTIONS,
HAND_INDEX_FINGER_CONNECTIONS, HAND_MIDDLE_FINGER_CONNECTIONS,
HAND_RING_FINGER_CONNECTIONS, HAND_PINKY_FINGER_CONNECTIONS
])
+3 -2
View File
@@ -30,6 +30,7 @@ from mediapipe.python.solutions import drawing_styles
from mediapipe.python.solutions import drawing_utils as mp_drawing
from mediapipe.python.solutions import hands as mp_hands
TEST_IMAGE_PATH = 'mediapipe/python/solutions/testdata'
DIFF_THRESHOLD = 15 # pixels
EXPECTED_HAND_COORDINATES_PREDICTION = [[[144, 345], [211, 323], [257, 286],
@@ -54,8 +55,8 @@ class HandsTest(parameterized.TestCase):
for hand_landmarks in results.multi_hand_landmarks:
mp_drawing.draw_landmarks(
frame, hand_landmarks, mp_hands.HAND_CONNECTIONS,
drawing_styles.get_default_hand_landmark_style(),
drawing_styles.get_default_hand_connection_style())
drawing_styles.get_default_hand_landmarks_style(),
drawing_styles.get_default_hand_connections_style())
path = os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] +
'_frame_{}.png'.format(idx))
cv2.imwrite(path, frame)
+6 -4
View File
@@ -41,11 +41,12 @@ from mediapipe.modules.holistic_landmark.calculators import roi_tracking_calcula
from mediapipe.python.solution_base import SolutionBase
from mediapipe.python.solutions import download_utils
# pylint: disable=unused-import
from mediapipe.python.solutions.face_mesh import FACE_CONNECTIONS
from mediapipe.python.solutions.hands import HAND_CONNECTIONS
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_CONTOURS
from mediapipe.python.solutions.face_mesh_connections import FACEMESH_TESSELATION
from mediapipe.python.solutions.hands import HandLandmark
from mediapipe.python.solutions.pose import POSE_CONNECTIONS
from mediapipe.python.solutions.hands_connections import HAND_CONNECTIONS
from mediapipe.python.solutions.pose import PoseLandmark
from mediapipe.python.solutions.pose_connections import POSE_CONNECTIONS
# pylint: enable=unused-import
BINARYPB_FILE_PATH = 'mediapipe/modules/holistic_landmark/holistic_landmark_cpu.binarypb'
@@ -103,6 +104,7 @@ class Holistic(SolutionBase):
side_inputs={
'model_complexity': model_complexity,
'smooth_landmarks': smooth_landmarks and not static_image_mode,
'smooth_segmentation': not static_image_mode,
},
calculator_params={
'poselandmarkcpu__ConstantSidePacketCalculator.packet': [
@@ -112,7 +114,7 @@ class Holistic(SolutionBase):
],
'poselandmarkcpu__posedetectioncpu__TensorsToDetectionsCalculator.min_score_thresh':
min_detection_confidence,
'poselandmarkcpu__poselandmarkbyroicpu__ThresholdingCalculator.threshold':
'poselandmarkcpu__poselandmarkbyroicpu__tensorstoposelandmarksandsegmentation__ThresholdingCalculator.threshold':
min_tracking_confidence,
},
outputs=[
+12 -10
View File
@@ -25,6 +25,7 @@ import numpy.testing as npt
# resources dependency
# undeclared dependency
from mediapipe.python.solutions import drawing_styles
from mediapipe.python.solutions import drawing_utils as mp_drawing
from mediapipe.python.solutions import holistic as mp_holistic
@@ -69,17 +70,18 @@ class PoseTest(parameterized.TestCase):
npt.assert_array_less(np.abs(array1 - array2), threshold)
def _annotate(self, frame: np.ndarray, results: NamedTuple, idx: int):
drawing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)
mp_drawing.draw_landmarks(
image=frame,
landmark_list=results.face_landmarks,
landmark_drawing_spec=drawing_spec)
mp_drawing.draw_landmarks(frame, results.left_hand_landmarks,
mp_holistic.HAND_CONNECTIONS)
mp_drawing.draw_landmarks(frame, results.right_hand_landmarks,
mp_holistic.HAND_CONNECTIONS)
mp_drawing.draw_landmarks(frame, results.pose_landmarks,
mp_holistic.POSE_CONNECTIONS)
frame,
results.face_landmarks,
mp_holistic.FACEMESH_TESSELATION,
landmark_drawing_spec=None,
connection_drawing_spec=drawing_styles
.get_default_face_mesh_tesselation_style())
mp_drawing.draw_landmarks(
frame,
results.pose_landmarks,
mp_holistic.POSE_CONNECTIONS,
landmark_drawing_spec=drawing_styles.get_default_pose_landmarks_style())
path = os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] +
'_frame_{}.png'.format(idx))
cv2.imwrite(path, frame)
+19 -41
View File
@@ -25,11 +25,13 @@ from mediapipe.calculators.core import constant_side_packet_calculator_pb2
# pylint: disable=unused-import
from mediapipe.calculators.core import gate_calculator_pb2
from mediapipe.calculators.core import split_vector_calculator_pb2
from mediapipe.calculators.image import warp_affine_calculator_pb2
from mediapipe.calculators.tensor import image_to_tensor_calculator_pb2
from mediapipe.calculators.tensor import inference_calculator_pb2
from mediapipe.calculators.tensor import tensors_to_classification_calculator_pb2
from mediapipe.calculators.tensor import tensors_to_detections_calculator_pb2
from mediapipe.calculators.tensor import tensors_to_landmarks_calculator_pb2
from mediapipe.calculators.tensor import tensors_to_segmentation_calculator_pb2
from mediapipe.calculators.tflite import ssd_anchors_calculator_pb2
from mediapipe.calculators.util import detections_to_rects_calculator_pb2
from mediapipe.calculators.util import landmarks_smoothing_calculator_pb2
@@ -41,9 +43,11 @@ from mediapipe.calculators.util import thresholding_calculator_pb2
from mediapipe.calculators.util import visibility_smoothing_calculator_pb2
from mediapipe.framework.tool import switch_container_pb2
# pylint: enable=unused-import
from mediapipe.python.solution_base import SolutionBase
from mediapipe.python.solutions import download_utils
# pylint: disable=unused-import
from mediapipe.python.solutions.pose_connections import POSE_CONNECTIONS
# pylint: enable=unused-import
class PoseLandmark(enum.IntEnum):
@@ -82,44 +86,8 @@ class PoseLandmark(enum.IntEnum):
LEFT_FOOT_INDEX = 31
RIGHT_FOOT_INDEX = 32
BINARYPB_FILE_PATH = 'mediapipe/modules/pose_landmark/pose_landmark_cpu.binarypb'
POSE_CONNECTIONS = frozenset([
(PoseLandmark.NOSE, PoseLandmark.RIGHT_EYE_INNER),
(PoseLandmark.RIGHT_EYE_INNER, PoseLandmark.RIGHT_EYE),
(PoseLandmark.RIGHT_EYE, PoseLandmark.RIGHT_EYE_OUTER),
(PoseLandmark.RIGHT_EYE_OUTER, PoseLandmark.RIGHT_EAR),
(PoseLandmark.NOSE, PoseLandmark.LEFT_EYE_INNER),
(PoseLandmark.LEFT_EYE_INNER, PoseLandmark.LEFT_EYE),
(PoseLandmark.LEFT_EYE, PoseLandmark.LEFT_EYE_OUTER),
(PoseLandmark.LEFT_EYE_OUTER, PoseLandmark.LEFT_EAR),
(PoseLandmark.MOUTH_RIGHT, PoseLandmark.MOUTH_LEFT),
(PoseLandmark.RIGHT_SHOULDER, PoseLandmark.LEFT_SHOULDER),
(PoseLandmark.RIGHT_SHOULDER, PoseLandmark.RIGHT_ELBOW),
(PoseLandmark.RIGHT_ELBOW, PoseLandmark.RIGHT_WRIST),
(PoseLandmark.RIGHT_WRIST, PoseLandmark.RIGHT_PINKY),
(PoseLandmark.RIGHT_WRIST, PoseLandmark.RIGHT_INDEX),
(PoseLandmark.RIGHT_WRIST, PoseLandmark.RIGHT_THUMB),
(PoseLandmark.RIGHT_PINKY, PoseLandmark.RIGHT_INDEX),
(PoseLandmark.LEFT_SHOULDER, PoseLandmark.LEFT_ELBOW),
(PoseLandmark.LEFT_ELBOW, PoseLandmark.LEFT_WRIST),
(PoseLandmark.LEFT_WRIST, PoseLandmark.LEFT_PINKY),
(PoseLandmark.LEFT_WRIST, PoseLandmark.LEFT_INDEX),
(PoseLandmark.LEFT_WRIST, PoseLandmark.LEFT_THUMB),
(PoseLandmark.LEFT_PINKY, PoseLandmark.LEFT_INDEX),
(PoseLandmark.RIGHT_SHOULDER, PoseLandmark.RIGHT_HIP),
(PoseLandmark.LEFT_SHOULDER, PoseLandmark.LEFT_HIP),
(PoseLandmark.RIGHT_HIP, PoseLandmark.LEFT_HIP),
(PoseLandmark.RIGHT_HIP, PoseLandmark.RIGHT_KNEE),
(PoseLandmark.LEFT_HIP, PoseLandmark.LEFT_KNEE),
(PoseLandmark.RIGHT_KNEE, PoseLandmark.RIGHT_ANKLE),
(PoseLandmark.LEFT_KNEE, PoseLandmark.LEFT_ANKLE),
(PoseLandmark.RIGHT_ANKLE, PoseLandmark.RIGHT_HEEL),
(PoseLandmark.LEFT_ANKLE, PoseLandmark.LEFT_HEEL),
(PoseLandmark.RIGHT_HEEL, PoseLandmark.RIGHT_FOOT_INDEX),
(PoseLandmark.LEFT_HEEL, PoseLandmark.LEFT_FOOT_INDEX),
(PoseLandmark.RIGHT_ANKLE, PoseLandmark.RIGHT_FOOT_INDEX),
(PoseLandmark.LEFT_ANKLE, PoseLandmark.LEFT_FOOT_INDEX),
])
def _download_oss_pose_landmark_model(model_complexity):
@@ -147,6 +115,8 @@ class Pose(SolutionBase):
static_image_mode=False,
model_complexity=1,
smooth_landmarks=True,
enable_segmentation=False,
smooth_segmentation=True,
min_detection_confidence=0.5,
min_tracking_confidence=0.5):
"""Initializes a MediaPipe Pose object.
@@ -160,6 +130,11 @@ class Pose(SolutionBase):
smooth_landmarks: Whether to filter landmarks across different input
images to reduce jitter. See details in
https://solutions.mediapipe.dev/pose#smooth_landmarks.
enable_segmentation: Whether to predict segmentation mask. See details in
https://solutions.mediapipe.dev/pose#enable_segmentation.
smooth_segmentation: Whether to filter segmentation across different input
images to reduce jitter. See details in
https://solutions.mediapipe.dev/pose#smooth_segmentation.
min_detection_confidence: Minimum confidence value ([0.0, 1.0]) for person
detection to be considered successful. See details in
https://solutions.mediapipe.dev/pose#min_detection_confidence.
@@ -173,6 +148,9 @@ class Pose(SolutionBase):
side_inputs={
'model_complexity': model_complexity,
'smooth_landmarks': smooth_landmarks and not static_image_mode,
'enable_segmentation': enable_segmentation,
'smooth_segmentation':
smooth_segmentation and not static_image_mode,
},
calculator_params={
'ConstantSidePacketCalculator.packet': [
@@ -180,12 +158,12 @@ class Pose(SolutionBase):
.ConstantSidePacketCalculatorOptions.ConstantSidePacket(
bool_value=not static_image_mode)
],
'poselandmarkcpu__posedetectioncpu__TensorsToDetectionsCalculator.min_score_thresh':
'posedetectioncpu__TensorsToDetectionsCalculator.min_score_thresh':
min_detection_confidence,
'poselandmarkcpu__poselandmarkbyroicpu__ThresholdingCalculator.threshold':
'poselandmarkbyroicpu__tensorstoposelandmarksandsegmentation__ThresholdingCalculator.threshold':
min_tracking_confidence,
},
outputs=['pose_landmarks', 'pose_world_landmarks'])
outputs=['pose_landmarks', 'pose_world_landmarks', 'segmentation_mask'])
def process(self, image: np.ndarray) -> NamedTuple:
"""Processes an RGB image and returns the pose landmarks on the most prominent person detected.
@@ -0,0 +1,22 @@
# Copyright 2021 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""MediaPipe Pose connections."""
POSE_CONNECTIONS = frozenset([(0, 1), (1, 2), (2, 3), (3, 7), (0, 4), (4, 5),
(5, 6), (6, 8), (9, 10), (11, 12), (11, 13),
(13, 15), (15, 17), (15, 19), (15, 21), (17, 19),
(12, 14), (14, 16), (16, 18), (16, 20), (16, 22),
(18, 20), (11, 23), (12, 24), (23, 24), (23, 25),
(24, 26), (25, 27), (26, 28), (27, 29), (28, 30),
(29, 31), (30, 32), (27, 31), (28, 32)])
+87 -22
View File
@@ -15,7 +15,9 @@
import json
import os
# pylint: disable=unused-import
import tempfile
# pylint: enable=unused-import
from typing import NamedTuple
from absl.testing import absltest
@@ -23,9 +25,11 @@ from absl.testing import parameterized
import cv2
import numpy as np
import numpy.testing as npt
from PIL import Image
# resources dependency
# undeclared dependency
from mediapipe.python.solutions import drawing_styles
from mediapipe.python.solutions import drawing_utils as mp_drawing
from mediapipe.python.solutions import pose as mp_pose
@@ -56,6 +60,7 @@ EXPECTED_POSE_WORLD_LANDMARKS = np.array([
[0.69, 0.49, -0.04], [-0.48, 0.47, -0.02], [0.72, 0.52, -0.04],
[-0.48, 0.51, -0.02], [0.8, 0.5, -0.14], [-0.59, 0.52, -0.11],
])
IOU_THRESHOLD = 0.85 # percents
class PoseTest(parameterized.TestCase):
@@ -72,13 +77,64 @@ class PoseTest(parameterized.TestCase):
def _assert_diff_less(self, array1, array2, threshold):
npt.assert_array_less(np.abs(array1 - array2), threshold)
def _get_output_path(self, name):
return os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] + name)
def _annotate(self, frame: np.ndarray, results: NamedTuple, idx: int):
mp_drawing.draw_landmarks(frame, results.pose_landmarks,
mp_pose.POSE_CONNECTIONS)
path = os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] +
'_frame_{}.png'.format(idx))
mp_drawing.draw_landmarks(
frame,
results.pose_landmarks,
mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=drawing_styles.get_default_pose_landmarks_style())
path = self._get_output_path('_frame_{}.png'.format(idx))
cv2.imwrite(path, frame)
def _annotate_segmentation(self, segmentation, expected_segmentation,
idx: int):
path = self._get_output_path('_segmentation_{}.png'.format(idx))
self._segmentation_to_rgb(segmentation).save(path)
path = self._get_output_path('_segmentation_diff_{}.png'.format(idx))
self._segmentation_diff_to_rgb(
expected_segmentation, segmentation).save(path)
def _rgb_to_segmentation(self, img, back_color=(255, 0, 0),
front_color=(0, 0, 255)):
img = np.array(img)
# Check all pixels are either front or back.
is_back = (img == back_color).all(axis=2)
is_front = (img == front_color).all(axis=2)
np.logical_or(is_back, is_front).all()
segm = np.zeros(img.shape[:2], dtype=np.uint8)
segm[is_front] = 1
return segm
def _segmentation_to_rgb(self, segm, back_color=(255, 0, 0),
front_color=(0, 0, 255)):
height, width = segm.shape
img = np.zeros((height, width, 3), dtype=np.uint8)
img[:, :] = back_color
img[segm == 1] = front_color
return Image.fromarray(img)
def _segmentation_iou(self, segm_expected, segm_actual):
intersection = segm_expected * segm_actual
expected_dot = segm_expected * segm_expected
actual_dot = segm_actual * segm_actual
eps = np.finfo(np.float32).eps
result = intersection.sum() / (expected_dot.sum() +
actual_dot.sum() -
intersection.sum() + eps)
return result
def _segmentation_diff_to_rgb(self, segm_expected, segm_actual,
expected_color=(0, 255, 0),
actual_color=(255, 0, 0)):
height, width = segm_expected.shape
img = np.zeros((height, width, 3), dtype=np.uint8)
img[np.logical_and(segm_expected == 1, segm_actual == 0)] = expected_color
img[np.logical_and(segm_expected == 0, segm_actual == 1)] = actual_color
return Image.fromarray(img)
def test_invalid_image_shape(self):
with mp_pose.Pose() as pose:
with self.assertRaisesRegex(
@@ -86,11 +142,12 @@ class PoseTest(parameterized.TestCase):
pose.process(np.arange(36, dtype=np.uint8).reshape(3, 3, 4))
def test_blank_image(self):
with mp_pose.Pose() as pose:
with mp_pose.Pose(enable_segmentation=True) as pose:
image = np.zeros([100, 100, 3], dtype=np.uint8)
image.fill(255)
results = pose.process(image)
self.assertIsNone(results.pose_landmarks)
self.assertIsNone(results.segmentation_mask)
@parameterized.named_parameters(('static_lite', True, 0, 3),
('static_full', True, 1, 3),
@@ -100,13 +157,23 @@ class PoseTest(parameterized.TestCase):
('video_heavy', False, 2, 3))
def test_on_image(self, static_image_mode, model_complexity, num_frames):
image_path = os.path.join(os.path.dirname(__file__), 'testdata/pose.jpg')
expected_segmentation_path = os.path.join(
os.path.dirname(__file__), 'testdata/pose_segmentation.png')
image = cv2.imread(image_path)
expected_segmentation = self._rgb_to_segmentation(
Image.open(expected_segmentation_path).convert('RGB'))
with mp_pose.Pose(static_image_mode=static_image_mode,
model_complexity=model_complexity) as pose:
model_complexity=model_complexity,
enable_segmentation=True) as pose:
for idx in range(num_frames):
results = pose.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
segmentation = results.segmentation_mask.round().astype(np.uint8)
# TODO: Add rendering of world 3D when supported.
self._annotate(image.copy(), results, idx)
self._annotate_segmentation(segmentation, expected_segmentation, idx)
self._assert_diff_less(
self._landmarks_list_to_array(results.pose_landmarks,
image.shape)[:, :2],
@@ -114,13 +181,14 @@ class PoseTest(parameterized.TestCase):
self._assert_diff_less(
self._world_landmarks_list_to_array(results.pose_world_landmarks),
EXPECTED_POSE_WORLD_LANDMARKS, WORLD_DIFF_THRESHOLD)
self.assertGreaterEqual(
self._segmentation_iou(expected_segmentation, segmentation),
IOU_THRESHOLD)
@parameterized.named_parameters(
('full', 1, 'pose_squats.full.npz'))
def test_on_video(self, model_complexity, expected_name):
"""Tests pose models on a video."""
# If set to `True` will dump actual predictions to .npz and JSON files.
dump_predictions = False
# Set threshold for comparing actual and expected predictions in pixels.
diff_threshold = 15
world_diff_threshold = 0.1
@@ -160,21 +228,18 @@ class PoseTest(parameterized.TestCase):
actual = np.array(actual_per_frame)
actual_world = np.array(actual_world_per_frame)
if dump_predictions:
# Dump .npz
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
np.savez(tmp_file, predictions=actual, predictions_world=actual_world)
print('Predictions saved as .npz to {}'.format(tmp_file.name))
# Dump actual .npz.
npz_path = self._get_output_path(expected_name)
np.savez(npz_path, predictions=actual, predictions_world=actual_world)
# Dump JSON
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
with open(tmp_file.name, 'w') as fl:
dump_data = {
'predictions': np.around(actual, 3).tolist(),
'predictions_world': np.around(actual_world, 3).tolist()
}
fl.write(json.dumps(dump_data, indent=2, separators=(',', ': ')))
print('Predictions saved as JSON to {}'.format(tmp_file.name))
# Dump actual JSON.
json_path = self._get_output_path(expected_name.replace('.npz', '.json'))
with open(json_path, 'w') as fl:
dump_data = {
'predictions': np.around(actual, 3).tolist(),
'predictions_world': np.around(actual_world, 3).tolist()
}
fl.write(json.dumps(dump_data, indent=2, separators=(',', ': ')))
# Validate actual vs. expected landmarks.
expected = np.load(expected_path)['predictions']