Project import generated by Copybara.
GitOrigin-RevId: 08c2016a4df5aef571b464a4d4491f38c6b2af10
This commit is contained in:
@@ -72,5 +72,6 @@ cc_library(
|
||||
"//mediapipe/modules/pose_detection:pose_detection_cpu",
|
||||
"//mediapipe/modules/pose_landmark:pose_landmark_by_roi_cpu",
|
||||
"//mediapipe/modules/pose_landmark:pose_landmark_cpu",
|
||||
"//mediapipe/modules/selfie_segmentation:selfie_segmentation_cpu",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -21,3 +21,4 @@ import mediapipe.python.solutions.hands
|
||||
import mediapipe.python.solutions.holistic
|
||||
import mediapipe.python.solutions.objectron
|
||||
import mediapipe.python.solutions.pose
|
||||
import mediapipe.python.solutions.selfie_segmentation
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"""MediaPipe solution drawing utils."""
|
||||
|
||||
import math
|
||||
from typing import List, Tuple, Union
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import cv2
|
||||
import dataclasses
|
||||
@@ -116,7 +116,7 @@ def draw_detection(
|
||||
def draw_landmarks(
|
||||
image: np.ndarray,
|
||||
landmark_list: landmark_pb2.NormalizedLandmarkList,
|
||||
connections: List[Tuple[int, int]] = None,
|
||||
connections: Optional[List[Tuple[int, int]]] = None,
|
||||
landmark_drawing_spec: DrawingSpec = DrawingSpec(color=RED_COLOR),
|
||||
connection_drawing_spec: DrawingSpec = DrawingSpec()):
|
||||
"""Draws the landmarks and the connections on the image.
|
||||
|
||||
@@ -56,7 +56,8 @@ class FaceDetectionTest(absltest.TestCase):
|
||||
self.assertIsNone(results.detections)
|
||||
|
||||
def test_face(self):
|
||||
image_path = os.path.join(os.path.dirname(__file__), 'testdata/face.jpg')
|
||||
image_path = os.path.join(os.path.dirname(__file__),
|
||||
'testdata/portrait.jpg')
|
||||
image = cv2.imread(image_path)
|
||||
with mp_faces.FaceDetection(min_detection_confidence=0.5) as faces:
|
||||
for idx in range(5):
|
||||
|
||||
@@ -96,7 +96,8 @@ class FaceMeshTest(parameterized.TestCase):
|
||||
@parameterized.named_parameters(('static_image_mode', True, 1),
|
||||
('video_mode', False, 5))
|
||||
def test_face(self, static_image_mode: bool, num_frames: int):
|
||||
image_path = os.path.join(os.path.dirname(__file__), 'testdata/face.jpg')
|
||||
image_path = os.path.join(os.path.dirname(__file__),
|
||||
'testdata/portrait.jpg')
|
||||
image = cv2.imread(image_path)
|
||||
with mp_faces.FaceMesh(
|
||||
static_image_mode=static_image_mode,
|
||||
|
||||
@@ -30,18 +30,18 @@ from mediapipe.python.solutions import drawing_utils as mp_drawing
|
||||
from mediapipe.python.solutions import pose as mp_pose
|
||||
|
||||
TEST_IMAGE_PATH = 'mediapipe/python/solutions/testdata'
|
||||
DIFF_THRESHOLD = 30 # pixels
|
||||
EXPECTED_POSE_LANDMARKS = np.array([[460, 287], [469, 277], [472, 276],
|
||||
[475, 276], [464, 277], [463, 277],
|
||||
[463, 276], [492, 277], [472, 277],
|
||||
[471, 295], [465, 295], [542, 323],
|
||||
[448, 318], [619, 319], [372, 313],
|
||||
[695, 316], [296, 308], [717, 313],
|
||||
[273, 304], [718, 304], [280, 298],
|
||||
[709, 307], [289, 303], [521, 470],
|
||||
[459, 466], [626, 533], [364, 500],
|
||||
[704, 616], [347, 614], [710, 631],
|
||||
[357, 633], [737, 625], [306, 639]])
|
||||
DIFF_THRESHOLD = 15 # pixels
|
||||
EXPECTED_POSE_LANDMARKS = np.array([[460, 283], [467, 273], [471, 273],
|
||||
[474, 273], [465, 273], [465, 273],
|
||||
[466, 273], [491, 277], [480, 277],
|
||||
[470, 294], [465, 294], [545, 319],
|
||||
[453, 329], [622, 323], [375, 316],
|
||||
[696, 316], [299, 307], [719, 316],
|
||||
[278, 306], [721, 311], [274, 304],
|
||||
[713, 313], [283, 306], [520, 476],
|
||||
[467, 471], [612, 550], [358, 490],
|
||||
[701, 613], [349, 611], [709, 624],
|
||||
[363, 630], [730, 633], [303, 628]])
|
||||
|
||||
|
||||
class PoseTest(parameterized.TestCase):
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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 Selfie Segmentation."""
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
import numpy as np
|
||||
# The following imports are needed because python pb2 silently discards
|
||||
# unknown protobuf fields.
|
||||
# pylint: disable=unused-import
|
||||
from mediapipe.calculators.core import constant_side_packet_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_segmentation_calculator_pb2
|
||||
from mediapipe.calculators.util import local_file_contents_calculator_pb2
|
||||
from mediapipe.framework.tool import switch_container_pb2
|
||||
# pylint: enable=unused-import
|
||||
|
||||
from mediapipe.python.solution_base import SolutionBase
|
||||
|
||||
BINARYPB_FILE_PATH = 'mediapipe/modules/selfie_segmentation/selfie_segmentation_cpu.binarypb'
|
||||
|
||||
|
||||
class SelfieSegmentation(SolutionBase):
|
||||
"""MediaPipe Selfie Segmentation.
|
||||
|
||||
MediaPipe Selfie Segmentation processes an RGB image and returns a
|
||||
segmentation mask.
|
||||
|
||||
Please refer to
|
||||
https://solutions.mediapipe.dev/selfie_segmentation#python-solution-api for
|
||||
usage examples.
|
||||
"""
|
||||
|
||||
def __init__(self, model_selection=0):
|
||||
"""Initializes a MediaPipe Selfie Segmentation object.
|
||||
|
||||
Args:
|
||||
model_selection: 0 or 1. 0 to select a general-purpose model, and 1 to
|
||||
select a model more optimized for landscape images. See details in
|
||||
https://solutions.mediapipe.dev/selfie_segmentation#model_selection.
|
||||
"""
|
||||
super().__init__(
|
||||
binary_graph_path=BINARYPB_FILE_PATH,
|
||||
side_inputs={
|
||||
'model_selection': model_selection,
|
||||
},
|
||||
outputs=['segmentation_mask'])
|
||||
|
||||
def process(self, image: np.ndarray) -> NamedTuple:
|
||||
"""Processes an RGB image and returns a segmentation mask.
|
||||
|
||||
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 object with a "segmentation_mask" field that contains a float
|
||||
type 2d np array representing the mask.
|
||||
"""
|
||||
|
||||
return super().process(input_data={'image': image})
|
||||
@@ -0,0 +1,68 @@
|
||||
# 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.
|
||||
"""Tests for mediapipe.python.solutions.selfie_segmentation."""
|
||||
|
||||
import os
|
||||
|
||||
from absl.testing import absltest
|
||||
from absl.testing import parameterized
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# resources dependency
|
||||
# undeclared dependency
|
||||
from mediapipe.python.solutions import selfie_segmentation as mp_selfie_segmentation
|
||||
|
||||
TEST_IMAGE_PATH = 'mediapipe/python/solutions/testdata'
|
||||
|
||||
|
||||
class SelfieSegmentationTest(parameterized.TestCase):
|
||||
|
||||
def _draw(self, frame: np.ndarray, mask: np.ndarray):
|
||||
frame = np.minimum(frame, np.stack((mask,) * 3, axis=-1))
|
||||
path = os.path.join(tempfile.gettempdir(), self.id().split('.')[-1] + '.png')
|
||||
cv2.imwrite(path, frame)
|
||||
|
||||
def test_invalid_image_shape(self):
|
||||
with mp_selfie_segmentation.SelfieSegmentation() as selfie_segmentation:
|
||||
with self.assertRaisesRegex(
|
||||
ValueError, 'Input image must contain three channel rgb data.'):
|
||||
selfie_segmentation.process(
|
||||
np.arange(36, dtype=np.uint8).reshape(3, 3, 4))
|
||||
|
||||
def test_blank_image(self):
|
||||
with mp_selfie_segmentation.SelfieSegmentation() as selfie_segmentation:
|
||||
image = np.zeros([100, 100, 3], dtype=np.uint8)
|
||||
image.fill(255)
|
||||
results = selfie_segmentation.process(image)
|
||||
normalized_segmentation_mask = (results.segmentation_mask *
|
||||
255).astype(int)
|
||||
self.assertLess(np.amax(normalized_segmentation_mask), 1)
|
||||
|
||||
@parameterized.named_parameters(('general', 0), ('landscape', 1))
|
||||
def test_segmentation(self, model_selection):
|
||||
image_path = os.path.join(os.path.dirname(__file__),
|
||||
'testdata/portrait.jpg')
|
||||
image = cv2.imread(image_path)
|
||||
with mp_selfie_segmentation.SelfieSegmentation(
|
||||
model_selection=model_selection) as selfie_segmentation:
|
||||
results = selfie_segmentation.process(
|
||||
cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
||||
normalized_segmentation_mask = (results.segmentation_mask *
|
||||
255).astype(int)
|
||||
self._draw(image.copy(), normalized_segmentation_mask)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
absltest.main()
|
||||
Reference in New Issue
Block a user