Project import generated by Copybara.

GitOrigin-RevId: 9295f8ea2339edb71073695ed4fb3fded2f48c60
This commit is contained in:
MediaPipe Team
2020-08-13 01:32:08 -04:00
committed by chuoling
parent 6b0ab0e012
commit d7c287c4e9
248 changed files with 10356 additions and 5328 deletions
+50
View File
@@ -0,0 +1,50 @@
# Copyright 2020 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.
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
licenses(["notice"]) # Apache 2.0
cc_library(
name = "builtin_calculators",
deps = [
"//mediapipe/calculators/core:pass_through_calculator",
"//mediapipe/graphs/pose_tracking:upper_body_pose_tracking_cpu_deps",
],
)
pybind_extension(
name = "_framework_bindings",
srcs = ["framework_bindings.cc"],
linkopts = [
"-lopencv_core",
"-lopencv_imgproc",
"-lopencv_highgui",
"-lopencv_video",
"-lopencv_features2d",
"-lopencv_calib3d",
"-lopencv_imgcodecs",
],
deps = [
":builtin_calculators",
"//mediapipe/python/pybind:calculator_graph",
"//mediapipe/python/pybind:image_frame",
"//mediapipe/python/pybind:matrix",
"//mediapipe/python/pybind:packet",
"//mediapipe/python/pybind:packet_creator",
"//mediapipe/python/pybind:packet_getter",
"//mediapipe/python/pybind:resource_util",
"//mediapipe/python/pybind:timestamp",
],
)
+26
View File
@@ -0,0 +1,26 @@
# Copyright 2020 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 Python API."""
from mediapipe.python._framework_bindings import resource_util
from mediapipe.python._framework_bindings.calculator_graph import CalculatorGraph
from mediapipe.python._framework_bindings.calculator_graph import GraphInputStreamAddMode
from mediapipe.python._framework_bindings.image_frame import ImageFormat
from mediapipe.python._framework_bindings.image_frame import ImageFrame
from mediapipe.python._framework_bindings.matrix import Matrix
from mediapipe.python._framework_bindings.packet import Packet
from mediapipe.python._framework_bindings.timestamp import Timestamp
import mediapipe.python.packet_creator
import mediapipe.python.packet_getter
+178
View File
@@ -0,0 +1,178 @@
# Copyright 2020 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.
# Lint as: python3
"""Tests for mediapipe.python._framework_bindings.calculator_graph."""
# Dependency imports
from absl.testing import absltest
import mediapipe.python as mp
from google.protobuf import text_format
from mediapipe.framework import calculator_pb2
class GraphTest(absltest.TestCase):
def testInvalidBinaryGraphFile(self):
with self.assertRaisesRegex(FileNotFoundError, 'No such file or directory'):
mp.CalculatorGraph(binary_graph_path='/tmp/abc.binarypb')
def testInvalidNodeConfig(self):
text_config = """
node {
calculator: 'PassThroughCalculator'
input_stream: 'in'
input_stream: 'in'
output_stream: 'out'
}
"""
config_proto = calculator_pb2.CalculatorGraphConfig()
text_format.Parse(text_config, config_proto)
with self.assertRaisesRegex(
ValueError,
'Input and output streams to PassThroughCalculator must use matching tags and indexes.'
):
mp.CalculatorGraph(graph_config=config_proto)
def testInvalidCalculatorType(self):
text_config = """
node {
calculator: 'SomeUnknownCalculator'
input_stream: 'in'
output_stream: 'out'
}
"""
config_proto = calculator_pb2.CalculatorGraphConfig()
text_format.Parse(text_config, config_proto)
with self.assertRaisesRegex(
RuntimeError, 'Unable to find Calculator \"SomeUnknownCalculator\"'):
mp.CalculatorGraph(graph_config=config_proto)
def testGraphInitializedWithProtoConfig(self):
text_config = """
max_queue_size: 1
input_stream: 'in'
output_stream: 'out'
node {
calculator: 'PassThroughCalculator'
input_stream: 'in'
output_stream: 'out'
}
"""
config_proto = calculator_pb2.CalculatorGraphConfig()
text_format.Parse(text_config, config_proto)
graph = mp.CalculatorGraph(graph_config=config_proto)
hello_world_packet = mp.packet_creator.create_string('hello world')
out = []
graph = mp.CalculatorGraph(graph_config=config_proto)
graph.observe_output_stream('out', lambda _, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet, timestamp=0)
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet.at(1))
graph.close()
self.assertEqual(graph.graph_input_stream_add_mode,
mp.GraphInputStreamAddMode.WAIT_TILL_NOT_FULL)
self.assertEqual(graph.max_queue_size, 1)
self.assertFalse(graph.has_error())
self.assertLen(out, 2)
self.assertEqual(out[0].timestamp, 0)
self.assertEqual(out[1].timestamp, 1)
self.assertEqual(mp.packet_getter.get_str(out[0]), 'hello world')
self.assertEqual(mp.packet_getter.get_str(out[1]), 'hello world')
def testGraphInitializedWithTextConfig(self):
text_config = """
max_queue_size: 1
input_stream: 'in'
output_stream: 'out'
node {
calculator: 'PassThroughCalculator'
input_stream: 'in'
output_stream: 'out'
}
"""
hello_world_packet = mp.packet_creator.create_string('hello world')
out = []
graph = mp.CalculatorGraph(graph_config=text_config)
graph.observe_output_stream('out', lambda _, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet.at(0))
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet, timestamp=1)
graph.close()
self.assertEqual(graph.graph_input_stream_add_mode,
mp.GraphInputStreamAddMode.WAIT_TILL_NOT_FULL)
self.assertEqual(graph.max_queue_size, 1)
self.assertFalse(graph.has_error())
self.assertLen(out, 2)
self.assertEqual(out[0].timestamp, 0)
self.assertEqual(out[1].timestamp, 1)
self.assertEqual(mp.packet_getter.get_str(out[0]), 'hello world')
self.assertEqual(mp.packet_getter.get_str(out[1]), 'hello world')
def testInsertPacketsWithSameTimestamp(self):
text_config = """
max_queue_size: 1
input_stream: 'in'
output_stream: 'out'
node {
calculator: 'PassThroughCalculator'
input_stream: 'in'
output_stream: 'out'
}
"""
config_proto = calculator_pb2.CalculatorGraphConfig()
text_format.Parse(text_config, config_proto)
hello_world_packet = mp.packet_creator.create_string('hello world')
out = []
graph = mp.CalculatorGraph(graph_config=config_proto)
graph.observe_output_stream('out', lambda _, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet.at(0))
graph.wait_until_idle()
graph.add_packet_to_input_stream(
stream='in', packet=hello_world_packet.at(0))
with self.assertRaisesRegex(
ValueError, 'Current minimum expected timestamp is 1 but received 0.'):
graph.wait_until_idle()
def testSidePacketGraph(self):
text_config = """
node {
calculator: 'StringToUint64Calculator'
input_side_packet: "string"
output_side_packet: "number"
}
"""
config_proto = calculator_pb2.CalculatorGraphConfig()
text_format.Parse(text_config, config_proto)
graph = mp.CalculatorGraph(graph_config=config_proto)
graph.start_run(
input_side_packets={'string': mp.packet_creator.create_string('42')})
graph.wait_until_done()
self.assertFalse(graph.has_error())
self.assertEqual(
mp.packet_getter.get_uint(graph.get_output_side_packet('number')), 42)
if __name__ == '__main__':
absltest.main()
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/calculator_graph.h"
#include "mediapipe/python/pybind/image_frame.h"
#include "mediapipe/python/pybind/matrix.h"
#include "mediapipe/python/pybind/packet.h"
#include "mediapipe/python/pybind/packet_creator.h"
#include "mediapipe/python/pybind/packet_getter.h"
#include "mediapipe/python/pybind/resource_util.h"
#include "mediapipe/python/pybind/timestamp.h"
namespace mediapipe {
namespace python {
PYBIND11_MODULE(_framework_bindings, m) {
ResourceUtilSubmodule(&m);
ImageFrameSubmodule(&m);
MatrixSubmodule(&m);
TimestampSubmodule(&m);
PacketSubmodule(&m);
PacketCreatorSubmodule(&m);
PacketGetterSubmodule(&m);
CalculatorGraphSubmodule(&m);
}
} // namespace python
} // namespace mediapipe
+145
View File
@@ -0,0 +1,145 @@
# Copyright 2020 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._framework_bindings.image_frame."""
import random
from absl.testing import absltest
import cv2
import numpy as np
import PIL.Image
import mediapipe.python as mp
# TODO: Add unit tests specifically for memory management.
class ImageFrameTest(absltest.TestCase):
def testCreateImageFrameFromGrayCvMat(self):
w, h = random.randrange(3, 100), random.randrange(3, 100)
mat = cv2.cvtColor(
np.random.randint(2**8 - 1, size=(h, w, 3), dtype=np.uint8),
cv2.COLOR_RGB2GRAY)
mat[2, 2] = 42
image_frame = mp.ImageFrame(image_format=mp.ImageFormat.GRAY8, data=mat)
self.assertTrue(np.array_equal(mat, image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'index dimension mismatch'):
print(image_frame[w, h, 1])
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[w, h])
self.assertEqual(42, image_frame[2, 2])
def testCreateImageFrameFromRgbCvMat(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
mat = cv2.cvtColor(
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
cv2.COLOR_RGB2BGR)
mat[2, 2, 1] = 42
image_frame = mp.ImageFrame(image_format=mp.ImageFormat.SRGB, data=mat)
self.assertTrue(np.array_equal(mat, image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[w, h, channels])
self.assertEqual(42, image_frame[2, 2, 1])
def testCreateImageFrameFromRgb48CvMat(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
mat = cv2.cvtColor(
np.random.randint(2**16 - 1, size=(h, w, channels), dtype=np.uint16),
cv2.COLOR_RGB2BGR)
mat[2, 2, 1] = 42
image_frame = mp.ImageFrame(image_format=mp.ImageFormat.SRGB48, data=mat)
self.assertTrue(np.array_equal(mat, image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[w, h, channels])
self.assertEqual(42, image_frame[2, 2, 1])
def testCreateImageFrameFromGrayPilImage(self):
w, h = random.randrange(3, 100), random.randrange(3, 100)
img = PIL.Image.fromarray(
np.random.randint(2**8 - 1, size=(h, w), dtype=np.uint8), 'L')
image_frame = mp.ImageFrame(
image_format=mp.ImageFormat.GRAY8, data=np.asarray(img))
self.assertTrue(np.array_equal(np.asarray(img), image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'index dimension mismatch'):
print(image_frame[w, h, 1])
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[w, h])
def testCreateImageFrameFromRgbPilImage(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
img = PIL.Image.fromarray(
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
'RGB')
image_frame = mp.ImageFrame(
image_format=mp.ImageFormat.SRGB, data=np.asarray(img))
self.assertTrue(np.array_equal(np.asarray(img), image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[w, h, channels])
def testCreateImageFrameFromRgba64PilImage(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 4
img = PIL.Image.fromarray(
np.random.randint(2**16 - 1, size=(h, w, channels), dtype=np.uint16),
'RGBA')
image_frame = mp.ImageFrame(
image_format=mp.ImageFormat.SRGBA64,
data=np.asarray(img, dtype=np.uint16))
self.assertTrue(np.array_equal(np.asarray(img), image_frame.numpy_view()))
with self.assertRaisesRegex(IndexError, 'out of bounds'):
print(image_frame[1000, 1000, 1000])
def testImageFrameNumbyView(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
mat = cv2.cvtColor(
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
cv2.COLOR_RGB2BGR)
image_frame = mp.ImageFrame(image_format=mp.ImageFormat.SRGB, data=mat)
output_ndarray = image_frame.numpy_view()
self.assertTrue(np.array_equal(mat, image_frame.numpy_view()))
# The output of numpy_view() is a reference to the internal data and it's
# unwritable after creation.
with self.assertRaisesRegex(ValueError,
'assignment destination is read-only'):
output_ndarray[0, 0, 0] = 0
copied_ndarray = np.copy(output_ndarray)
copied_ndarray[0, 0, 0] = 0
def testCroppedGray8Image(self):
w, h = random.randrange(20, 100), random.randrange(20, 100)
channels, offset = 3, 10
mat = cv2.cvtColor(
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
cv2.COLOR_RGB2GRAY)
image_frame = mp.ImageFrame(
image_format=mp.ImageFormat.GRAY8,
data=mat[offset:-offset, offset:-offset])
self.assertTrue(
np.array_equal(mat[offset:-offset, offset:-offset],
image_frame.numpy_view()))
def testCroppedRGBImage(self):
w, h = random.randrange(20, 100), random.randrange(20, 100)
channels, offset = 3, 10
mat = cv2.cvtColor(
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
cv2.COLOR_RGB2BGR)
image_frame = mp.ImageFrame(
image_format=mp.ImageFormat.SRGB,
data=mat[offset:-offset, offset:-offset, :])
self.assertTrue(
np.array_equal(mat[offset:-offset, offset:-offset, :],
image_frame.numpy_view()))
if __name__ == '__main__':
absltest.main()
+126
View File
@@ -0,0 +1,126 @@
# Copyright 2020 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.
# Lint as: python3
"""The public facing packet creator APIs."""
from typing import List, Union
import numpy as np
from google.protobuf import message
from mediapipe.python._framework_bindings import _packet_creator
from mediapipe.python._framework_bindings import image_frame
from mediapipe.python._framework_bindings import packet
create_string = _packet_creator.create_string
create_bool = _packet_creator.create_bool
create_int = _packet_creator.create_int
create_int8 = _packet_creator.create_int8
create_int16 = _packet_creator.create_int16
create_int32 = _packet_creator.create_int32
create_int64 = _packet_creator.create_int64
create_uint8 = _packet_creator.create_uint8
create_uint16 = _packet_creator.create_uint16
create_uint32 = _packet_creator.create_uint32
create_uint64 = _packet_creator.create_uint64
create_float = _packet_creator.create_float
create_double = _packet_creator.create_double
create_int_array = _packet_creator.create_int_array
create_float_array = _packet_creator.create_float_array
create_int_vector = _packet_creator.create_int_vector
create_float_vector = _packet_creator.create_float_vector
create_string_vector = _packet_creator.create_string_vector
create_packet_vector = _packet_creator.create_packet_vector
create_string_to_packet_map = _packet_creator.create_string_to_packet_map
create_matrix = _packet_creator.create_matrix
def create_image_frame(
data: Union[image_frame.ImageFrame, np.ndarray],
*,
image_format: image_frame.ImageFormat = None) -> packet.Packet:
"""Create a MediaPipe ImageFrame packet.
A MediaPipe ImageFrame packet can be created from either the raw pixel data
represented as a numpy array with one of the uint8, uint16, and float data
types or an existing MediaPipe ImageFrame object. The data will be realigned
and copied into an ImageFrame object inside of the packet.
Args:
data: A MediaPipe ImageFrame object or the raw pixel data that is
represnted as a numpy ndarray.
image_format: One of the image_frame.ImageFormat enum types.
Returns:
A MediaPipe ImageFrame Packet.
Raises:
ValueError:
i) When "data" is a numpy ndarray, "image_format" is not provided.
ii) When "data" is an ImageFrame object, the "image_format" arg doesn't
match the image format of the "data" ImageFrame object.
TypeError: If "image format" doesn't match "data" array's data type.
Examples:
np_array = np.random.randint(255, size=(321, 123, 3), dtype=np.uint8)
image_frame_packet = mp.packet_creator.create_image_frame(
image_format=mp.ImageFormat.SRGB, data=np_array)
image_frame = mp.ImageFrame(image_format=mp.ImageFormat.SRGB, data=np_array)
image_frame_packet = mp.packet_creator.create_image_frame(image_frame)
"""
if isinstance(data, image_frame.ImageFrame):
if image_format is not None and data.image_format != image_format:
raise ValueError(
'The provided image_format doesn\'t match the one from the data arg.')
# pylint:disable=protected-access
return _packet_creator._create_image_frame_with_copy(data)
# pylint:enable=protected-access
else:
if image_format is None:
raise ValueError('Please provide \'image_format\' with \'data\'.')
# pylint:disable=protected-access
return _packet_creator._create_image_frame_with_copy(image_format, data)
# pylint:enable=protected-access
def create_proto(proto_message: message.Message) -> packet.Packet:
"""Create a MediaPipe protobuf message packet.
Args:
proto_message: A Python protobuf message.
Returns:
A MediaPipe protobuf message Packet.
Raises:
RuntimeError: If the protobuf message type is not registered in MediaPipe.
Examples:
detection = detection_pb2.Detection()
text_format.Parse('score: 0.5', detection)
packet = mp.packet_creator.create_proto(detection)
output_detection = mp.packet_getter.get_proto(packet)
"""
# pylint:disable=protected-access
return _packet_creator._create_proto(proto_message.DESCRIPTOR.full_name,
proto_message.SerializeToString())
# pylint:enable=protected-access
def create_proto_vector(message_list: List[message.Message]) -> packet.Packet:
raise NotImplementedError('create_proto_vector is not implemented.')
+117
View File
@@ -0,0 +1,117 @@
# Copyright 2020 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.
# Lint as: python3
"""The public facing packet getter APIs."""
from typing import List, Type
from google.protobuf import message
from google.protobuf import symbol_database
from mediapipe.python._framework_bindings import _packet_getter
from mediapipe.python._framework_bindings import packet as mp_packet
get_str = _packet_getter.get_str
get_bytes = _packet_getter.get_bytes
get_bool = _packet_getter.get_bool
get_int = _packet_getter.get_int
get_uint = _packet_getter.get_uint
get_float = _packet_getter.get_float
get_int_list = _packet_getter.get_int_list
get_float_list = _packet_getter.get_float_list
get_str_list = _packet_getter.get_str_list
get_packet_list = _packet_getter.get_packet_list
get_str_to_packet_dict = _packet_getter.get_str_to_packet_dict
get_image_frame = _packet_getter.get_image_frame
get_matrix = _packet_getter.get_matrix
def get_proto(packet: mp_packet.Packet) -> Type[message.Message]:
"""Get the content of a MediaPipe proto Packet as a proto message.
Args:
packet: A MediaPipe proto Packet.
Returns:
A proto message.
Raises:
TypeError: If the message descriptor can't be found by type name.
Examples:
detection = detection_pb2.Detection()
text_format.Parse('score: 0.5', detection)
proto_packet = mp.packet_creator.create_proto(detection)
output_proto = mp.packet_getter.get_proto(proto_packet)
"""
# pylint:disable=protected-access
proto_type_name = _packet_getter._get_proto_type_name(packet)
# pylint:enable=protected-access
try:
descriptor = symbol_database.Default().pool.FindMessageTypeByName(
proto_type_name)
except KeyError:
raise TypeError('Can not find message descriptor by type name: %s' %
proto_type_name)
message_class = symbol_database.Default().GetPrototype(descriptor)
# pylint:disable=protected-access
serialized_proto = _packet_getter._get_serialized_proto(packet)
# pylint:enable=protected-access
proto_message = message_class()
proto_message.ParseFromString(serialized_proto)
return proto_message
def get_proto_list(packet: mp_packet.Packet) -> List[message.Message]:
"""Get the content of a MediaPipe proto vector Packet as a proto message list.
Args:
packet: A MediaPipe proto vector Packet.
Returns:
A proto message list.
Raises:
TypeError: If the message descriptor can't be found by type name.
Examples:
proto_list = mp.packet_getter.get_proto_list(protos_packet)
"""
# pylint:disable=protected-access
vector_size = _packet_getter._get_proto_vector_size(packet)
# pylint:enable=protected-access
# Return empty list if the proto vector is empty.
if vector_size == 0:
return []
# pylint:disable=protected-access
proto_type_name = _packet_getter._get_proto_vector_element_type_name(packet)
# pylint:enable=protected-access
try:
descriptor = symbol_database.Default().pool.FindMessageTypeByName(
proto_type_name)
except KeyError:
raise TypeError('Can not find message descriptor by type name: %s' %
proto_type_name)
message_class = symbol_database.Default().GetPrototype(descriptor)
# pylint:disable=protected-access
serialized_protos = _packet_getter._get_serialized_proto_list(packet)
# pylint:enable=protected-access
proto_message_list = []
for serialized_proto in serialized_protos:
proto_message = message_class()
proto_message.ParseFromString(serialized_proto)
proto_message_list.append(proto_message)
return proto_message_list
+349
View File
@@ -0,0 +1,349 @@
# Copyright 2020 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._framework_bindings.packet."""
import gc
import random
import sys
from absl.testing import absltest
import numpy as np
import mediapipe.python as mp
from google.protobuf import text_format
from mediapipe.framework.formats import detection_pb2
class PacketTest(absltest.TestCase):
def testEmptyPacket(self):
p = mp.Packet()
self.assertTrue(p.is_empty())
def testBooleanPacket(self):
p = mp.packet_creator.create_bool(True)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_bool(p), True)
self.assertEqual(p.timestamp, 0)
def testIntPacket(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_int(2**32)
p = mp.packet_creator.create_int(42)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p), 42)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_int(np.intc(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testInt8Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_int8(2**7)
p = mp.packet_creator.create_int8(2**7 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p), 2**7 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_int8(np.int8(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testInt16Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_int16(2**15)
p = mp.packet_creator.create_int16(2**15 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p), 2**15 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_int16(np.int16(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testInt32Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_int32(2**31)
p = mp.packet_creator.create_int32(2**31 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p), 2**31 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_int32(np.int32(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testInt64Packet(self):
p = mp.packet_creator.create_int64(2**63 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p), 2**63 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_int64(np.int64(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_int(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testUint8Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_uint8(2**8)
p = mp.packet_creator.create_uint8(2**8 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p), 2**8 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_uint8(np.uint8(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testUint16Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_uint16(2**16)
p = mp.packet_creator.create_uint16(2**16 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p), 2**16 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_uint16(np.uint16(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testUint32Packet(self):
with self.assertRaisesRegex(OverflowError, 'execeeds the maximum value'):
p = mp.packet_creator.create_uint32(2**32)
p = mp.packet_creator.create_uint32(2**32 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p), 2**32 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_uint32(np.uint32(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testUint64Packet(self):
p = mp.packet_creator.create_uint64(2**64 - 1)
p.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p), 2**64 - 1)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_uint64(np.uint64(1))
p2.timestamp = 0
self.assertEqual(mp.packet_getter.get_uint(p2), 1)
self.assertEqual(p2.timestamp, 0)
def testFloatPacket(self):
p = mp.packet_creator.create_float(0.42)
p.timestamp = 0
self.assertAlmostEqual(mp.packet_getter.get_float(p), 0.42)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_float(np.float(0.42))
p2.timestamp = 0
self.assertAlmostEqual(mp.packet_getter.get_float(p2), 0.42)
self.assertEqual(p2.timestamp, 0)
def testDoublePacket(self):
p = mp.packet_creator.create_double(0.42)
p.timestamp = 0
self.assertAlmostEqual(mp.packet_getter.get_float(p), 0.42)
self.assertEqual(p.timestamp, 0)
p2 = mp.packet_creator.create_double(np.double(0.42))
p2.timestamp = 0
self.assertAlmostEqual(mp.packet_getter.get_float(p2), 0.42)
self.assertEqual(p2.timestamp, 0)
def testDetectionProtoPacket(self):
detection = detection_pb2.Detection()
text_format.Parse('score: 0.5', detection)
p = mp.packet_creator.create_proto(detection).at(100)
def testStringPacket(self):
p = mp.packet_creator.create_string('abc').at(100)
self.assertEqual(mp.packet_getter.get_str(p), 'abc')
self.assertEqual(p.timestamp, 100)
p.timestamp = 200
self.assertEqual(p.timestamp, 200)
def testBytesPacket(self):
p = mp.packet_creator.create_string(b'xd0\xba\xd0').at(300)
self.assertEqual(mp.packet_getter.get_bytes(p), b'xd0\xba\xd0')
self.assertEqual(p.timestamp, 300)
def testIntArrayPacket(self):
p = mp.packet_creator.create_int_array([1, 2, 3]).at(100)
self.assertEqual(p.timestamp, 100)
def testFloatArrayPacket(self):
p = mp.packet_creator.create_float_array([0.1, 0.2, 0.3]).at(100)
self.assertEqual(p.timestamp, 100)
def testIntVectorPacket(self):
p = mp.packet_creator.create_int_vector([1, 2, 3]).at(100)
self.assertEqual(mp.packet_getter.get_int_list(p), [1, 2, 3])
self.assertEqual(p.timestamp, 100)
def testFloatVectorPacket(self):
p = mp.packet_creator.create_float_vector([0.1, 0.2, 0.3]).at(100)
output_list = mp.packet_getter.get_float_list(p)
self.assertAlmostEqual(output_list[0], 0.1)
self.assertAlmostEqual(output_list[1], 0.2)
self.assertAlmostEqual(output_list[2], 0.3)
self.assertEqual(p.timestamp, 100)
def testStringVectorPacket(self):
p = mp.packet_creator.create_string_vector(['a', 'b', 'c']).at(100)
output_list = mp.packet_getter.get_str_list(p)
self.assertEqual(output_list[0], 'a')
self.assertEqual(output_list[1], 'b')
self.assertEqual(output_list[2], 'c')
self.assertEqual(p.timestamp, 100)
def testPacketVectorPacket(self):
p = mp.packet_creator.create_packet_vector([
mp.packet_creator.create_float(0.42),
mp.packet_creator.create_int(42),
mp.packet_creator.create_string('42')
]).at(100)
output_list = mp.packet_getter.get_packet_list(p)
self.assertAlmostEqual(mp.packet_getter.get_float(output_list[0]), 0.42)
self.assertEqual(mp.packet_getter.get_int(output_list[1]), 42)
self.assertEqual(mp.packet_getter.get_str(output_list[2]), '42')
self.assertEqual(p.timestamp, 100)
def testStringToPacketMapPacket(self):
p = mp.packet_creator.create_string_to_packet_map({
'float': mp.packet_creator.create_float(0.42),
'int': mp.packet_creator.create_int(42),
'string': mp.packet_creator.create_string('42')
}).at(100)
output_list = mp.packet_getter.get_str_to_packet_dict(p)
self.assertAlmostEqual(
mp.packet_getter.get_float(output_list['float']), 0.42)
self.assertEqual(mp.packet_getter.get_int(output_list['int']), 42)
self.assertEqual(mp.packet_getter.get_str(output_list['string']), '42')
self.assertEqual(p.timestamp, 100)
def testUint8ImageFramePacket(self):
uint8_img = np.random.randint(
2**8 - 1,
size=(random.randrange(3, 100), random.randrange(3, 100), 3),
dtype=np.uint8)
p = mp.packet_creator.create_image_frame(
mp.ImageFrame(image_format=mp.ImageFormat.SRGB, data=uint8_img))
output_image_frame = mp.packet_getter.get_image_frame(p)
self.assertTrue(np.array_equal(output_image_frame.numpy_view(), uint8_img))
def testUint16ImageFramePacket(self):
uint16_img = np.random.randint(
2**16 - 1,
size=(random.randrange(3, 100), random.randrange(3, 100), 4),
dtype=np.uint16)
p = mp.packet_creator.create_image_frame(
mp.ImageFrame(image_format=mp.ImageFormat.SRGBA64, data=uint16_img))
output_image_frame = mp.packet_getter.get_image_frame(p)
self.assertTrue(np.array_equal(output_image_frame.numpy_view(), uint16_img))
def testFloatImageFramePacket(self):
float_img = np.float32(
np.random.random_sample(
(random.randrange(3, 100), random.randrange(3, 100), 2)))
p = mp.packet_creator.create_image_frame(
mp.ImageFrame(image_format=mp.ImageFormat.VEC32F2, data=float_img))
output_image_frame = mp.packet_getter.get_image_frame(p)
self.assertTrue(np.allclose(output_image_frame.numpy_view(), float_img))
def testImageFramePacketCreationCopyMode(self):
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
rgb_data = np.random.randint(255, size=(h, w, channels), dtype=np.uint8)
# rgb_data is c_contiguous.
self.assertTrue(rgb_data.flags.c_contiguous)
initial_ref_count = sys.getrefcount(rgb_data)
p = mp.packet_creator.create_image_frame(
image_format=mp.ImageFormat.SRGB, data=rgb_data)
# copy mode doesn't increase the ref count of the data.
self.assertEqual(sys.getrefcount(rgb_data), initial_ref_count)
rgb_data = rgb_data[:, :, ::-1]
# rgb_data is now not c_contiguous. But, copy mode shouldn't be affected.
self.assertFalse(rgb_data.flags.c_contiguous)
initial_ref_count = sys.getrefcount(rgb_data)
p = mp.packet_creator.create_image_frame(
image_format=mp.ImageFormat.SRGB, data=rgb_data)
# copy mode doesn't increase the ref count of the data.
self.assertEqual(sys.getrefcount(rgb_data), initial_ref_count)
output_frame = mp.packet_getter.get_image_frame(p)
self.assertEqual(output_frame.height, h)
self.assertEqual(output_frame.width, w)
self.assertEqual(output_frame.channels, channels)
self.assertTrue(np.array_equal(output_frame.numpy_view(), rgb_data))
del p
del output_frame
gc.collect()
# Destroying the packet also doesn't affect the ref count becuase of the
# copy mode.
self.assertEqual(sys.getrefcount(rgb_data), initial_ref_count)
def testImageFramePacketCopyConstuctionWithCropping(self):
w, h, channels = random.randrange(40, 100), random.randrange(40, 100), 3
channels, offset = 3, 10
rgb_data = np.random.randint(255, size=(h, w, channels), dtype=np.uint8)
initial_ref_count = sys.getrefcount(rgb_data)
p = mp.packet_creator.create_image_frame(
image_format=mp.ImageFormat.SRGB,
data=rgb_data[offset:-offset, offset:-offset, :])
# copy mode doesn't increase the ref count of the data.
self.assertEqual(sys.getrefcount(rgb_data), initial_ref_count)
output_frame = mp.packet_getter.get_image_frame(p)
self.assertEqual(output_frame.height, h - 2 * offset)
self.assertEqual(output_frame.width, w - 2 * offset)
self.assertEqual(output_frame.channels, channels)
self.assertTrue(
np.array_equal(rgb_data[offset:-offset, offset:-offset, :],
output_frame.numpy_view()))
del p
del output_frame
gc.collect()
# Destroying the packet also doesn't affect the ref count becuase of the
# copy mode.
self.assertEqual(sys.getrefcount(rgb_data), initial_ref_count)
def testMatrixPacket(self):
np_matrix = np.array([[.1, .2, .3], [.4, .5, .6]])
initial_ref_count = sys.getrefcount(np_matrix)
p = mp.packet_creator.create_matrix(np_matrix)
# Copy mode should not increase the ref count of np_matrix.
self.assertEqual(initial_ref_count, sys.getrefcount(np_matrix))
output_matrix = mp.packet_getter.get_matrix(p)
del np_matrix
gc.collect()
self.assertTrue(
np.allclose(output_matrix, np.array([[.1, .2, .3], [.4, .5, .6]])))
def testMatrixPacketWithNonCContiguousData(self):
np_matrix = np.array([[.1, .2, .3], [.4, .5, .6]])[:, ::-1]
# np_matrix is not c_contiguous.
self.assertFalse(np_matrix.flags.c_contiguous)
p = mp.packet_creator.create_matrix(np_matrix)
initial_ref_count = sys.getrefcount(np_matrix)
# Copy mode should not increase the ref count of np_matrix.
self.assertEqual(initial_ref_count, sys.getrefcount(np_matrix))
output_matrix = mp.packet_getter.get_matrix(p)
del np_matrix
gc.collect()
self.assertTrue(
np.allclose(output_matrix,
np.array([[.1, .2, .3], [.4, .5, .6]])[:, ::-1]))
if __name__ == '__main__':
absltest.main()
+141
View File
@@ -0,0 +1,141 @@
# Copyright 2020 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.
load("@pybind11_bazel//:build_defs.bzl", "pybind_library")
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/python:__subpackages__"])
pybind_library(
name = "calculator_graph",
srcs = ["calculator_graph.cc"],
hdrs = ["calculator_graph.h"],
deps = [
":util",
"//mediapipe/framework:calculator_cc_proto",
"//mediapipe/framework:calculator_graph",
"//mediapipe/framework:packet",
"//mediapipe/framework/port:file_helpers",
"//mediapipe/framework/port:map_util",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status",
"//mediapipe/framework/tool:calculator_graph_template_cc_proto",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
],
)
pybind_library(
name = "image_frame",
srcs = ["image_frame.cc"],
hdrs = ["image_frame.h"],
deps = [
":image_frame_util",
":util",
],
)
pybind_library(
name = "image_frame_util",
hdrs = ["image_frame_util.h"],
deps = [
"//mediapipe/framework/formats:image_format_cc_proto",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/port:logging",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
],
)
pybind_library(
name = "matrix",
srcs = ["matrix.cc"],
hdrs = ["matrix.h"],
deps = [
"//mediapipe/framework/formats:matrix",
],
)
pybind_library(
name = "packet",
srcs = ["packet.cc"],
hdrs = ["packet.h"],
deps = [
":util",
"//mediapipe/framework:packet",
"//mediapipe/framework:timestamp",
],
)
pybind_library(
name = "packet_creator",
srcs = ["packet_creator.cc"],
hdrs = ["packet_creator.h"],
deps = [
":image_frame_util",
":util",
"//mediapipe/framework:packet",
"//mediapipe/framework:timestamp",
"//mediapipe/framework/formats:matrix",
"//mediapipe/framework/port:integral_types",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
],
)
pybind_library(
name = "packet_getter",
srcs = ["packet_getter.cc"],
hdrs = ["packet_getter.h"],
deps = [
":image_frame_util",
":util",
"//mediapipe/framework:packet",
"//mediapipe/framework:timestamp",
"//mediapipe/framework/formats:matrix",
"//mediapipe/framework/port:integral_types",
],
)
pybind_library(
name = "timestamp",
srcs = ["timestamp.cc"],
hdrs = ["timestamp.h"],
deps = [
":util",
"//mediapipe/framework:timestamp",
"@com_google_absl//absl/strings",
],
)
pybind_library(
name = "resource_util",
srcs = ["resource_util.cc"],
hdrs = ["resource_util.h"],
deps = [
"//mediapipe/util:resource_util",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/strings",
],
)
pybind_library(
name = "util",
hdrs = ["util.h"],
deps = [
"//mediapipe/framework:timestamp",
"//mediapipe/framework/port:status",
],
)
+466
View File
@@ -0,0 +1,466 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/calculator_graph.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/calculator.pb.h"
#include "mediapipe/framework/calculator_graph.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/file_helpers.h"
#include "mediapipe/framework/port/map_util.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/tool/calculator_graph_template.pb.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/embed.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
namespace mediapipe {
namespace python {
template <typename T>
T ParseProto(const py::object& proto_object) {
T proto;
if (!ParseTextProto<T>(proto_object.str(), &proto)) {
throw RaisePyError(
PyExc_RuntimeError,
absl::StrCat("Failed to parse: ", std::string(proto_object.str()))
.c_str());
}
return proto;
}
namespace py = pybind11;
void CalculatorGraphSubmodule(pybind11::module* module) {
py::module m = module->def_submodule("calculator_graph",
"MediaPipe calculator graph module.");
using GraphInputStreamAddMode =
mediapipe::CalculatorGraph::GraphInputStreamAddMode;
py::enum_<GraphInputStreamAddMode>(m, "GraphInputStreamAddMode")
.value("WAIT_TILL_NOT_FULL", GraphInputStreamAddMode::WAIT_TILL_NOT_FULL)
.value("ADD_IF_NOT_FULL", GraphInputStreamAddMode::ADD_IF_NOT_FULL)
.export_values();
// Calculator Graph
py::class_<CalculatorGraph> calculator_graph(
m, "CalculatorGraph", R"doc(The primary API for the MediaPipe Framework.
MediaPipe processing takes place inside a graph, which defines packet flow
paths between nodes. A graph can have any number of inputs and outputs, and
data flow can branch and merge. Generally data flows forward, but backward
loops are possible.)doc");
// TODO: Support graph initialization with graph templates and
// subgraph.
calculator_graph.def(
py::init([](py::args args, py::kwargs kwargs) {
if (!args.empty()) {
throw RaisePyError(PyExc_RuntimeError,
"Invalid position input arguments.");
}
bool init_with_binary_graph = false;
bool init_with_graph_proto = false;
CalculatorGraphConfig graph_config_proto;
for (const auto& kw : kwargs) {
const std::string& key = kw.first.cast<std::string>();
if (key == "binary_graph_path") {
init_with_binary_graph = true;
std::string file_name(kw.second.cast<py::object>().str());
auto status = file::Exists(file_name);
if (!status.ok()) {
throw RaisePyError(PyExc_FileNotFoundError,
status.message().data());
}
std::string graph_config_string;
RaisePyErrorIfNotOk(
file::GetContents(file_name, &graph_config_string));
if (!graph_config_proto.ParseFromArray(
graph_config_string.c_str(),
graph_config_string.length())) {
throw RaisePyError(
PyExc_RuntimeError,
absl::StrCat("Failed to parse the binary graph: ", file_name)
.c_str());
}
} else if (key == "graph_config") {
init_with_graph_proto = true;
graph_config_proto =
ParseProto<CalculatorGraphConfig>(kw.second.cast<py::object>());
} else {
throw RaisePyError(
PyExc_RuntimeError,
absl::StrCat("Unknown kwargs input argument: ", key).c_str());
}
}
if (!(init_with_binary_graph ^ init_with_graph_proto)) {
throw RaisePyError(
PyExc_ValueError,
"Please provide \'binary_graph\' to initialize the graph with"
" binary graph or provide \'graph_config\' to initialize the "
" with graph config proto.");
}
auto calculator_graph = absl::make_unique<CalculatorGraph>();
RaisePyErrorIfNotOk(calculator_graph->Initialize(graph_config_proto));
return calculator_graph.release();
}),
R"doc(Initialize CalculatorGraph object.
Args:
binary_graph_path: The path to a binary mediapipe graph file (.binarypb).
graph_config: A single CalculatorGraphConfig proto message or its text proto
format.
Raises:
FileNotFoundError: If the binary graph file can't be found.
ValueError: If the input arguments prvoided are more than needed or the
graph validation process contains error.
)doc");
// TODO: Return a Python CalculatorGraphConfig instead.
calculator_graph.def_property_readonly(
"config",
[](const CalculatorGraph& self) { return self.Config().DebugString(); });
calculator_graph.def_property_readonly(
"serialized_config", [](const CalculatorGraph& self) {
return py::bytes(self.Config().SerializeAsString());
});
calculator_graph.def_property_readonly(
"max_queue_size",
[](CalculatorGraph* self) { return self->GetMaxInputStreamQueueSize(); });
calculator_graph.def_property(
"graph_input_stream_add_mode",
[](const CalculatorGraph& self) {
return self.GetGraphInputStreamAddMode();
},
[](CalculatorGraph* self, CalculatorGraph::GraphInputStreamAddMode mode) {
self->SetGraphInputStreamAddMode(mode);
});
calculator_graph.def(
"add_packet_to_input_stream",
[](CalculatorGraph* self, const std::string& stream, const Packet& packet,
const Timestamp& timestamp) {
Timestamp packet_timestamp =
timestamp == Timestamp::Unset() ? packet.Timestamp() : timestamp;
if (!packet_timestamp.IsAllowedInStream()) {
throw RaisePyError(
PyExc_ValueError,
absl::StrCat(packet_timestamp.DebugString(),
" can't be the timestamp of a Packet in a stream.")
.c_str());
}
RaisePyErrorIfNotOk(
self->AddPacketToInputStream(stream, packet.At(packet_timestamp)));
},
R"doc(Add a packet to a graph input stream.
If the graph input stream add mode is ADD_IF_NOT_FULL, the packet will not be
added if any queue exceeds the max queue size specified by the graph config
and will raise a Python runtime error. The WAIT_TILL_NOT_FULL mode (default)
will block until the queues fall below the max queue size before adding the
packet. If the mode is max queue size is -1, then the packet is added
regardless of the sizes of the queues in the graph. The input stream must have
been specified in the configuration as a graph level input stream. On error,
nothing is added.
Args:
stream: The name of the graph input stream.
packet: The packet to be added into the input stream.
timestamp: The timestamp of the packet. If set, the original packet
timestamp will be overwritten.
Raises:
RuntimeError: If the stream is not a graph input stream or the packet can't
be added into the input stream due to the limited queue size or the wrong
packet type.
ValueError: If the timestamp of the Packet is invalid to be the timestamp of
a Packet in a stream.
Examples:
graph.add_packet_to_input_stream(
stream='in',
packet=packet_creator.create_string('hello world').at(0))
graph.add_packet_to_input_stream(
stream='in',
packet=packet_creator.create_string('hello world'),
timstamp=1)
)doc",
py::arg("stream"), py::arg("packet"),
py::arg("timestamp") = Timestamp::Unset());
calculator_graph.def(
"close_input_stream",
[](CalculatorGraph* self, const std::string& stream) {
RaisePyErrorIfNotOk(self->CloseInputStream(stream));
},
R"doc(Close the named graph input stream.
Args:
stream: The name of the stream to be closed.
Raises:
RuntimeError: If the stream is not a graph input stream.
)doc");
calculator_graph.def(
"close_all_packet_sources",
[](CalculatorGraph* self) {
RaisePyErrorIfNotOk(self->CloseAllPacketSources());
},
R"doc(Closes all the graph input streams and source calculator nodes.)doc");
calculator_graph.def(
"start_run",
[](CalculatorGraph* self, const pybind11::dict& input_side_packets) {
std::map<std::string, Packet> input_side_packet_map;
for (const auto& kv_pair : input_side_packets) {
InsertIfNotPresent(&input_side_packet_map,
kv_pair.first.cast<std::string>(),
kv_pair.second.cast<Packet>());
}
RaisePyErrorIfNotOk(self->StartRun(input_side_packet_map));
},
R"doc(Start a run of the calculator graph.
A non-blocking call to start a run of the graph and will return when the graph
is started. If input_side_packets is provided, the method will runs the graph
after adding the given extra input side packets.
start_run(), wait_until_done(), has_error(), add_packet_to_input_stream(), and
close() allow more control over the execution of the graph run. You can
insert packets directly into a stream while the graph is running.
Once start_run() has been called, the graph will continue to run until
wait_until_done() is called.
If start_run() returns an error, then the graph is not started and a
subsequent call to start_run() can be attempted.
Args:
input_side_packets: A dict maps from the input side packet names to the
packets.
Raises:
RuntimeError: If the start run occurs any error, e.g. the graph config has
errors, the calculator can't be found, and the streams are not properly
connected.
Examples:
graph = mp.CalculatorGraph(graph_config=video_process_graph)
graph.start_run(
input_side_packets={
'input_path': packet_creator.create_string('/tmp/input.video'),
'output_path': packet_creator.create_string('/tmp/output.video')
})
graph.close()
out = []
graph = mp.CalculatorGraph(graph_config=pass_through_graph)
graph.observe_output_stream('out',
lambda stream_name, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(0), timestamp=0)
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(1), timestamp=1)
graph.close()
)doc",
py::arg("input_side_packets") = (py::dict){});
calculator_graph.def(
"wait_until_done",
[](CalculatorGraph* self) { RaisePyErrorIfNotOk(self->WaitUntilDone()); },
R"doc(Wait for the current run to finish.
A blocking call to wait for the current run to finish (block the current
thread until all source calculators are stopped, all graph input streams have
been closed, and no more calculators can be run). This function can be called
only after start_run(),
Raises:
RuntimeError: If the graph occurs any error during the wait call.
Examples:
out = []
graph = mp.CalculatorGraph(graph_config=pass_through_graph)
graph.observe_output_stream('out', lambda stream_name, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(0), timestamp=0)
graph.close_all_packet_sources()
graph.wait_until_done()
)doc");
calculator_graph.def(
"wait_until_idle",
[](CalculatorGraph* self) { RaisePyErrorIfNotOk(self->WaitUntilIdle()); },
R"doc(Wait until the running graph is in the idle mode.
Wait until the running graph is in the idle mode, which is when nothing can
be scheduled and nothing is running in the worker threads. This function can
be called only after start_run().
NOTE: The graph must not have any source nodes because source nodes prevent
the running graph from becoming idle until the source nodes are done.
Raises:
RuntimeError: If the graph occurs any error during the wait call.
Examples:
out = []
graph = mp.CalculatorGraph(graph_config=pass_through_graph)
graph.observe_output_stream('out',
lambda stream_name, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(0), timestamp=0)
graph.wait_until_idle()
)doc");
calculator_graph.def(
"wait_for_observed_output",
[](CalculatorGraph* self) {
RaisePyErrorIfNotOk(self->WaitForObservedOutput());
},
R"doc(Wait until a packet is emitted on one of the observed output streams.
Returns immediately if a packet has already been emitted since the last
call to this function.
Raises:
RuntimeError:
If the graph occurs any error or the graph is terminated while waiting.
Examples:
out = []
graph = mp.CalculatorGraph(graph_config=pass_through_graph)
graph.observe_output_stream('out',
lambda stream_name, packet: out.append(packet))
graph.start_run()
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(0), timestamp=0)
graph.wait_for_observed_output()
value = packet_getter.get_int(out[0])
graph.add_packet_to_input_stream(
stream='in', packet=packet_creator.create_int(1), timestamp=1)
graph.wait_for_observed_output()
value = packet_getter.get_int(out[1])
)doc");
calculator_graph.def(
"has_error", [](const CalculatorGraph& self) { return self.HasError(); },
R"doc(Quick non-locking means of checking if the graph has encountered an error)doc");
calculator_graph.def(
"get_combined_error_message",
[](CalculatorGraph* self) {
::mediapipe::Status error_status;
if (self->GetCombinedErrors(&error_status) && !error_status.ok()) {
return error_status.ToString();
}
return std::string();
},
R"doc(Combines error messages as a single std::string.
Examples:
if graph.has_error():
print(graph.get_combined_error_message())
)doc");
// TODO: Support passing a single-argument lambda for convenience.
calculator_graph.def(
"observe_output_stream",
[](CalculatorGraph* self, const std::string& stream_name,
pybind11::function callback_fn) {
RaisePyErrorIfNotOk(self->ObserveOutputStream(
stream_name, [callback_fn, stream_name](const Packet& packet) {
callback_fn(stream_name, packet);
return mediapipe::OkStatus();
}));
},
R"doc(Observe the named output stream.
callback_fn will be invoked on every packet emitted by the output stream.
This method can only be called before start_run().
Args:
stream_name: The name of the output stream.
callback_fn: The callback function to invoke on every packet emitted by the
output stream.
Raises:
RuntimeError: If the calculator graph isn't initialized or the stream
doesn't exist.
Examples:
out = []
graph = mp.CalculatorGraph(graph_config=graph_config)
graph.observe_output_stream('out',
lambda stream_name, packet: out.append(packet))
)doc");
calculator_graph.def(
"close",
[](CalculatorGraph* self) {
RaisePyErrorIfNotOk(self->CloseAllPacketSources());
RaisePyErrorIfNotOk(self->WaitUntilDone());
},
R"doc(Close all the input sources and shutdown the graph.)doc");
calculator_graph.def(
"get_output_side_packet",
[](CalculatorGraph* self, const std::string& packet_name) {
auto status_or_packet = self->GetOutputSidePacket(packet_name);
RaisePyErrorIfNotOk(status_or_packet.status());
return status_or_packet.ValueOrDie();
},
R"doc(Get output side packet by name after the graph is done.
Args:
stream: The name of the outnput stream.
Raises:
RuntimeError: If the graph is still running or the output side packet is not
found or empty.
Examples:
graph = mp.CalculatorGraph(graph_config=graph_config)
graph.start_run()
graph.close()
output_side_packet = graph.get_output_side_packet('packet_name')
)doc",
py::return_value_policy::move);
}
} // namespace python
} // namespace mediapipe
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_CALCULATOR_GRAPH_H_
#define MEDIAPIPE_PYTHON_PYBIND_CALCULATOR_GRAPH_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void CalculatorGraphSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_CALCULATOR_GRAPH_H_
+326
View File
@@ -0,0 +1,326 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/image_frame_util.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/stl.h"
namespace mediapipe {
namespace python {
namespace {
template <typename T>
py::array GenerateContiguousDataArray(const ImageFrame& image_frame,
const py::object& py_object) {
std::vector<int> shape{image_frame.Height(), image_frame.Width()};
if (image_frame.NumberOfChannels() > 1) {
shape.push_back(image_frame.NumberOfChannels());
}
py::array_t<T, py::array::c_style> contiguous_data;
if (image_frame.IsContiguous()) {
// TODO: Create contiguous_data without copying ata.
// It's possible to achieve this with the help of py::capsule.
// Reference: https://github.com/pybind/pybind11/issues/1042,
contiguous_data = py::array_t<T, py::array::c_style>(
shape, reinterpret_cast<const T*>(image_frame.PixelData()));
} else {
auto contiguous_data_copy =
absl::make_unique<T[]>(image_frame.Width() * image_frame.Height() *
image_frame.NumberOfChannels());
image_frame.CopyToBuffer(contiguous_data_copy.get(),
image_frame.PixelDataSizeStoredContiguously());
auto capsule = py::capsule(contiguous_data_copy.get(), [](void* data) {
if (data) {
delete[] reinterpret_cast<T*>(data);
}
});
contiguous_data = py::array_t<T, py::array::c_style>(
shape, contiguous_data_copy.release(), capsule);
}
// In both cases, the underlying data is not writable in Python.
py::detail::array_proxy(contiguous_data.ptr())->flags &=
~py::detail::npy_api::NPY_ARRAY_WRITEABLE_;
return contiguous_data;
}
py::array GetContiguousDataAttr(const ImageFrame& image_frame,
const py::object& py_object) {
py::object get_data_attr =
py::getattr(py_object, "__contiguous_data", py::none());
if (image_frame.IsEmpty()) {
throw RaisePyError(PyExc_RuntimeError, "ImageFrame is unallocated.");
}
// If __contiguous_data attr already stores data, return the cached results.
if (!get_data_attr.is_none()) {
return get_data_attr.cast<py::array>();
}
switch (image_frame.ChannelSize()) {
case sizeof(uint8):
py_object.attr("__contiguous_data") =
GenerateContiguousDataArray<uint8>(image_frame, py_object);
break;
case sizeof(uint16):
py_object.attr("__contiguous_data") =
GenerateContiguousDataArray<uint16>(image_frame, py_object);
break;
case sizeof(float):
py_object.attr("__contiguous_data") =
GenerateContiguousDataArray<float>(image_frame, py_object);
break;
default:
throw RaisePyError(PyExc_RuntimeError,
"Unsupported image frame channel size. Data is not "
"uint8, uint16, or float?");
}
return py_object.attr("__contiguous_data").cast<py::array>();
}
template <typename T>
py::object GetValue(const ImageFrame& image_frame, const std::vector<int>& pos,
const py::object& py_object) {
py::array_t<T, py::array::c_style> output_array =
GetContiguousDataAttr(image_frame, py_object);
if (pos.size() == 2) {
return py::cast(static_cast<T>(output_array.at(pos[0], pos[1])));
} else if (pos.size() == 3) {
return py::cast(static_cast<T>(output_array.at(pos[0], pos[1], pos[2])));
}
return py::none();
}
} // namespace
namespace py = pybind11;
void ImageFrameSubmodule(pybind11::module* module) {
py::module m =
module->def_submodule("image_frame", "MediaPipe image frame module");
py::options options;
options.disable_function_signatures();
// ImageFormat
py::enum_<mediapipe::ImageFormat::Format> image_format(
m, "ImageFormat",
R"doc(An enum describing supported raw image formats.
SRGB: sRGB, interleaved: one byte for R, then one byte for G, then one byte
for B for each pixel.
SRGBA: sRGBA, interleaved: one byte for R, one byte for G, one byte for B, one
byte for alpha or unused.
SBGRA: sBGRA, interleaved: one byte for B, one byte for G, one byte for R, one
byte for alpha or unused.
GRAY8: Grayscale, one byte per pixel.
GRAY16: Grayscale, one uint16 per pixel.
SRGB48: sRGB, interleaved, each component is a uint16.
SRGBA64: sRGBA, interleaved, each component is a uint16.
VEC32F1: One float per pixel.
VEC32F2: Two floats per pixel.
)doc");
image_format.value("SRGB", mediapipe::ImageFormat::SRGB)
.value("SRGBA", mediapipe::ImageFormat::SRGBA)
.value("SBGRA", mediapipe::ImageFormat::SBGRA)
.value("GRAY8", mediapipe::ImageFormat::GRAY8)
.value("GRAY16", mediapipe::ImageFormat::GRAY16)
.value("SRGB48", mediapipe::ImageFormat::SRGB48)
.value("SRGBA64", mediapipe::ImageFormat::SRGBA64)
.value("VEC32F1", mediapipe::ImageFormat::VEC32F1)
.value("VEC32F2", mediapipe::ImageFormat::VEC32F2)
.export_values();
// ImageFrame
py::class_<ImageFrame> image_frame(
m, "ImageFrame",
R"doc(A container for storing an image or a video frame, in one of several formats.
Formats supported by ImageFrame are listed in the ImageFormat enum.
Pixels are encoded row-major in an interleaved fashion. ImageFrame supports
uint8, uint16, and float as its data types.
ImageFrame can be created by copying the data from a numpy ndarray that stores
the pixel data continuously. An ImageFrame may realign the input data on its
default alignment boundary during creation. The data in an ImageFrame will
become immutable after creation.
Creation examples:
import cv2
cv_mat = cv2.imread(input_file)[:, :, ::-1]
rgb_frame = mp.ImageFrame(format=ImageFormat.SRGB, data=cv_mat)
gray_frame = mp.ImageFrame(
format=ImageFormat.GRAY, data=cv2.cvtColor(cv_mat, cv2.COLOR_RGB2GRAY))
from PIL import Image
pil_img = Image.new('RGB', (60, 30), color = 'red')
image_frame = mp.ImageFrame(
format=mp.ImageFormat.SRGB, data=np.asarray(pil_img))
The pixel data in an ImageFrame can be retrieved as a numpy ndarray by calling
`ImageFrame.numpy_view()`. The returned numpy ndarray is a reference to the
internal data and itself is unwritable. If the callers want to modify the
numpy ndarray, it's required to obtain a copy of it.
Pixel data retrieval examples:
for channel in range(num_channel):
for col in range(width):
for row in range(height):
print(image_frame[row, col, channel])
output_ndarray = image_frame.numpy_view()
print(output_ndarray[0, 0, 0])
copied_ndarray = np.copy(output_ndarray)
copied_ndarray[0,0,0] = 0
)doc",
py::dynamic_attr());
image_frame
.def(
py::init([](mediapipe::ImageFormat::Format format,
const py::array_t<uint8, py::array::c_style>& data) {
if (format != mediapipe::ImageFormat::GRAY8 &&
format != mediapipe::ImageFormat::SRGB &&
format != mediapipe::ImageFormat::SRGBA) {
throw RaisePyError(PyExc_RuntimeError,
"uint8 image data should be one of the GRAY8, "
"SRGB, and SRGBA MediaPipe image formats.");
}
return CreateImageFrame<uint8>(format, data);
}),
R"doc(For uint8 data type, valid ImageFormat are GRAY8, SGRB, and SRGBA.)doc",
py::arg("image_format"), py::arg("data").noconvert())
.def(
py::init([](mediapipe::ImageFormat::Format format,
const py::array_t<uint16, py::array::c_style>& data) {
if (format != mediapipe::ImageFormat::GRAY16 &&
format != mediapipe::ImageFormat::SRGB48 &&
format != mediapipe::ImageFormat::SRGBA64) {
throw RaisePyError(
PyExc_RuntimeError,
"uint16 image data should be one of the GRAY16, "
"SRGB48, and SRGBA64 MediaPipe image formats.");
}
return CreateImageFrame<uint16>(format, data);
}),
R"doc(For uint16 data type, valid ImageFormat are GRAY16, SRGB48, and SRGBA64.)doc",
py::arg("image_format"), py::arg("data").noconvert())
.def(
py::init([](mediapipe::ImageFormat::Format format,
const py::array_t<float, py::array::c_style>& data) {
if (format != mediapipe::ImageFormat::VEC32F1 &&
format != mediapipe::ImageFormat::VEC32F2) {
throw RaisePyError(
PyExc_RuntimeError,
"float image data should be either VEC32F1 or VEC32F2 "
"MediaPipe image formats.");
}
return CreateImageFrame<float>(format, data);
}),
R"doc(For float data type, valid ImageFormat are VEC32F1 and VEC32F2.)doc",
py::arg("image_format"), py::arg("data").noconvert());
image_frame.def(
"numpy_view",
[](ImageFrame& self) {
py::object py_object =
py::cast(self, py::return_value_policy::reference);
return GetContiguousDataAttr(self, py_object);
},
R"doc(Return the image frame pixel data as an unwritable numpy ndarray.
Realign the pixel data to be stored contiguously and return a reference to the
unwritable numpy ndarray. If the callers want to modify the numpy array data,
it's required to obtain a copy of the ndarray.
Returns:
An unwritable numpy ndarray.
Examples:
output_ndarray = image_frame.numpy_view()
copied_ndarray = np.copy(output_ndarray)
copied_ndarray[0,0,0] = 0
)doc");
image_frame.def(
"__getitem__",
[](ImageFrame& self, const std::vector<int>& pos) {
if (pos.size() != 3 &&
!(pos.size() == 2 && self.NumberOfChannels() == 1)) {
throw RaisePyError(
PyExc_IndexError,
absl::StrCat("Invalid index dimension: ", pos.size()).c_str());
}
py::object py_object =
py::cast(self, py::return_value_policy::reference);
switch (self.ByteDepth()) {
case 1:
return GetValue<uint8>(self, pos, py_object);
case 2:
return GetValue<uint16>(self, pos, py_object);
case 4:
return GetValue<float>(self, pos, py_object);
default:
return py::object();
}
},
R"doc(Use the indexer operators to access pixel data.
Raises:
IndexError: If the index is invalid or out of bounds.
Examples:
for channel in range(num_channel):
for col in range(width):
for row in range(height):
print(image_frame[row, col, channel])
)doc");
image_frame
.def(
"is_contiguous", &ImageFrame::IsContiguous,
R"doc(Return True if the pixel data is stored contiguously (without any alignment padding areas).)doc")
.def("is_empty", &ImageFrame::IsEmpty,
R"doc(Return True if the pixel data is unallocated.)doc")
.def(
"is_aligned", &ImageFrame::IsAligned,
R"doc(Return True if each row of the data is aligned to alignment boundary, which must be 1 or a power of 2.
Args:
alignment_boundary: An integer.
Returns:
A boolean.
Examples:
image_frame.is_aligned(16)
)doc");
image_frame.def_property_readonly("width", &ImageFrame::Width)
.def_property_readonly("height", &ImageFrame::Height)
.def_property_readonly("channels", &ImageFrame::NumberOfChannels)
.def_property_readonly("byte_depth", &ImageFrame::ByteDepth)
.def_property_readonly("image_format", &ImageFrame::Format);
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_H_
#define MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void ImageFrameSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_H_
@@ -0,0 +1,61 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_UTIL_H_
#define MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_UTIL_H_
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/formats/image_format.pb.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/port/logging.h"
#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
namespace py = pybind11;
// TODO: Implement the reference mode of image frame creation, which
// takes a reference to the external data rather than copying it over.
// A possible solution is to have a custom PixelDataDeleter:
// The refcount of the numpy array will be increased when the image frame is
// created by taking a reference to the external numpy array data. Then, the
// custom PixelDataDeleter will decrease the refcount when the image frame gets
// destroyed and let Python GC does its job.
template <typename T>
std::unique_ptr<ImageFrame> CreateImageFrame(
mediapipe::ImageFormat::Format format,
const py::array_t<T, py::array::c_style>& data) {
int rows = data.shape()[0];
int cols = data.shape()[1];
int width_step = ImageFrame::NumberOfChannelsForFormat(format) *
ImageFrame::ByteDepthForFormat(format) * cols;
auto image_frame = absl::make_unique<ImageFrame>(
format, /*width=*/cols, /*height=*/rows, width_step,
static_cast<uint8*>(data.request().ptr),
ImageFrame::PixelDataDeleter::kNone);
auto image_frame_copy = absl::make_unique<ImageFrame>();
// Set alignment_boundary to kGlDefaultAlignmentBoundary so that both
// GPU and CPU can process it.
image_frame_copy->CopyFrom(*image_frame,
ImageFrame::kGlDefaultAlignmentBoundary);
return image_frame_copy;
}
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_IMAGE_FRAME_UTIL_H_
+36
View File
@@ -0,0 +1,36 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/matrix.h"
#include "mediapipe/framework/formats/matrix.h"
#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
namespace py = pybind11;
void MatrixSubmodule(pybind11::module* module) {
py::module m = module->def_submodule("matrix", "MediaPipe matrix module.");
py::class_<mediapipe::Matrix>(m, "Matrix")
.def(py::init(
// Pass by reference.
[](const Eigen::Ref<const Eigen::MatrixXf>& m) { return m; }));
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_MATRIX_H_
#define MEDIAPIPE_PYTHON_PYBIND_MATRIX_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void MatrixSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_MATRIX_H_
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/packet.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/timestamp.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
namespace py = pybind11;
void PacketSubmodule(pybind11::module* module) {
py::module m = module->def_submodule("packet", "MediaPipe packet module.");
py::class_<Packet> packet(
m, "Packet",
R"doc(The basic data flow unit of MediaPipe. A generic container class which can hold data of any type.
A packet consists of a numeric timestamp and a shared pointer to an immutable
payload. The payload can be of any C++ type (See packet_creator module for
the list of the Python types that are supported). The payload's type is also
referred to as the type of the packet. Packets are value classes and can be
copied and moved cheaply. Each copy shares ownership of the payload, with be
copied reference-counting semantics. Each copy has its own timestamp.
The preferred method of creating a Packet is to invoke the methods in the
"packet_creator" module. Packet contents can be retrieved by the methods in
the "packet_getter" module.
)doc");
packet.def(py::init(),
R"doc(Create an empty Packet, for which is_empty() is True and
timestamp() is Timestamp.unset. Calling packet getter methods on this Packet leads to runtime error.)doc");
packet.def(
"is_empty", &Packet::IsEmpty,
R"doc(Return true iff the Packet has been created using the default constructor Packet(), or is a copy of such a Packet.)doc");
packet.def(py::init<Packet const&>())
.def("at", [](Packet* self,
int64 ts_value) { return self->At(Timestamp(ts_value)); })
.def("at", [](Packet* self, Timestamp ts) { return self->At(ts); })
.def_property(
"timestamp", &Packet::Timestamp,
[](Packet* p, int64 ts_value) { *p = p->At(Timestamp(ts_value)); })
.def("__repr__", [](const Packet& self) {
return absl::StrCat(
"<mediapipe.Packet with timestamp: ",
TimestampValueString(self.Timestamp()),
self.IsEmpty()
? " and no data>"
: absl::StrCat(" and C++ type: ", self.DebugTypeName(), ">"));
});
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_PACKET_H_
#define MEDIAPIPE_PYTHON_PYBIND_PACKET_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void PacketSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_PACKET_H_
+645
View File
@@ -0,0 +1,645 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/packet_creator.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/formats/matrix.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/framework/timestamp.h"
#include "mediapipe/python/pybind/image_frame_util.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/eigen.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
namespace mediapipe {
namespace python {
namespace {
Packet CreateImageFramePacket(mediapipe::ImageFormat::Format format,
const py::array& data) {
if (format == mediapipe::ImageFormat::SRGB ||
format == mediapipe::ImageFormat::SRGBA ||
format == mediapipe::ImageFormat::GRAY8) {
return Adopt(CreateImageFrame<uint8>(format, data).release());
} else if (format == mediapipe::ImageFormat::GRAY16 ||
format == mediapipe::ImageFormat::SRGB48 ||
format == mediapipe::ImageFormat::SRGBA64) {
return Adopt(CreateImageFrame<uint16>(format, data).release());
} else if (format == mediapipe::ImageFormat::VEC32F1 ||
format == mediapipe::ImageFormat::VEC32F2) {
return Adopt(CreateImageFrame<float>(format, data).release());
}
throw RaisePyError(PyExc_RuntimeError,
absl::StrCat("Unsupported ImageFormat: ", format).c_str());
return Packet();
}
} // namespace
namespace py = pybind11;
void PublicPacketCreators(pybind11::module* m) {
m->def(
"create_string",
[](const std::string& data) { return MakePacket<std::string>(data); },
R"doc(Create a MediaPipe std::string Packet from a str.
Args:
data: A str.
Returns:
A MediaPipe std::string Packet.
Raises:
TypeError: If the input is not a str.
Examples:
packet = mp.packet_creator.create_string('abc')
data = mp.packet_getter.get_string(packet)
)doc",
py::return_value_policy::move);
m->def(
"create_string",
[](const py::bytes& data) { return MakePacket<std::string>(data); },
R"doc(Create a MediaPipe std::string Packet from a bytes object.
Args:
data: A bytes object.
Returns:
A MediaPipe std::string Packet.
Raises:
TypeError: If the input is not a bytes object.
Examples:
packet = mp.packet_creator.create_string(b'\xd0\xd0\xd0')
data = mp.packet_getter.get_bytes(packet)
)doc",
py::return_value_policy::move);
m->def(
"create_bool", [](bool data) { return MakePacket<bool>(data); },
R"doc(Create a MediaPipe bool Packet from a boolean object.
Args:
data: A boolean object.
Returns:
A MediaPipe bool Packet.
Raises:
TypeError: If the input is not a boolean object.
Examples:
packet = mp.packet_creator.create_bool(True)
data = mp.packet_getter.get_bool(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int",
[](int64 data) {
RaisePyErrorIfOverflow(data, INT_MIN, INT_MAX);
return MakePacket<int>(data);
},
R"doc(Create a MediaPipe int Packet from an integer.
Args:
data: An integer or a np.intc.
Returns:
A MediaPipe int Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is not an integer.
Examples:
packet = mp.packet_creator.create_int(0)
data = mp.packet_getter.get_int(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int8",
[](int64 data) {
RaisePyErrorIfOverflow(data, INT8_MIN, INT8_MAX);
return MakePacket<int8>(data);
},
R"doc(Create a MediaPipe int8 Packet from an integer.
Args:
data: An integer or a np.int8.
Returns:
A MediaPipe int8 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.int8.
Examples:
packet = mp.packet_creator.create_int8(2**7 - 1)
data = mp.packet_getter.get_int(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int16",
[](int64 data) {
RaisePyErrorIfOverflow(data, INT16_MIN, INT16_MAX);
return MakePacket<int16>(data);
},
R"doc(Create a MediaPipe int16 Packet from an integer.
Args:
data: An integer or a np.int16.
Returns:
A MediaPipe int16 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.int16.
Examples:
packet = mp.packet_creator.create_int16(2**15 - 1)
data = mp.packet_getter.get_int(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int32",
[](int64 data) {
RaisePyErrorIfOverflow(data, INT32_MIN, INT32_MAX);
return MakePacket<int32>(data);
},
R"doc(Create a MediaPipe int32 Packet from an integer.
Args:
data: An integer or a np.int32.
Returns:
A MediaPipe int32 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.int32.
Examples:
packet = mp.packet_creator.create_int32(2**31 - 1)
data = mp.packet_getter.get_int(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int64", [](int64 data) { return MakePacket<int64>(data); },
R"doc(Create a MediaPipe int64 Packet from an integer.
Args:
data: An integer or a np.int64.
Returns:
A MediaPipe int64 Packet.
Raises:
TypeError: If the input is neither an integer nor a np.int64.
Examples:
packet = mp.packet_creator.create_int64(2**63 - 1)
data = mp.packet_getter.get_int(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_uint8",
[](int64 data) {
RaisePyErrorIfOverflow(data, 0, UINT8_MAX);
return MakePacket<uint8>(data);
},
R"doc(Create a MediaPipe uint8 Packet from an integer.
Args:
data: An integer or a np.uint8.
Returns:
A MediaPipe uint8 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.uint8.
Examples:
packet = mp.packet_creator.create_uint8(2**8 - 1)
data = mp.packet_getter.get_uint(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_uint16",
[](int64 data) {
RaisePyErrorIfOverflow(data, 0, UINT16_MAX);
return MakePacket<uint16>(data);
},
R"doc(Create a MediaPipe uint16 Packet from an integer.
Args:
data: An integer or a np.uint16.
Returns:
A MediaPipe uint16 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.uint16.
Examples:
packet = mp.packet_creator.create_uint16(2**16 - 1)
data = mp.packet_getter.get_uint(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_uint32",
[](int64 data) {
RaisePyErrorIfOverflow(data, 0, UINT32_MAX);
return MakePacket<uint32>(data);
},
R"doc(Create a MediaPipe uint32 Packet from an integer.
Args:
data: An integer or a np.uint32.
Returns:
A MediaPipe uint32 Packet.
Raises:
OverflowError: If the input integer overflows.
TypeError: If the input is neither an integer nor a np.uint32.
Examples:
packet = mp.packet_creator.create_uint32(2**32 - 1)
data = mp.packet_getter.get_uint(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_uint64", [](uint64 data) { return MakePacket<uint64>(data); },
R"doc(Create a MediaPipe uint64 Packet from an integer.
Args:
data: An integer or a np.uint64.
Returns:
A MediaPipe uint64 Packet.
Raises:
TypeError: If the input is neither an integer nor a np.uint64.
Examples:
packet = mp.packet_creator.create_uint64(2**64 - 1)
data = mp.packet_getter.get_uint(packet)
)doc",
// py::arg().noconvert() won't allow this to accept np.uint64 data type.
py::arg(), py::return_value_policy::move);
m->def(
"create_float", [](float data) { return MakePacket<float>(data); },
R"doc(Create a MediaPipe float Packet from a float.
Args:
data: A float or a np.float.
Returns:
A MediaPipe float Packet.
Raises:
TypeError: If the input is neither a float nor a np.float.
Examples:
packet = mp.packet_creator.create_float(0.1)
data = mp.packet_getter.get_float(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_double", [](double data) { return MakePacket<double>(data); },
R"doc(Create a MediaPipe double Packet from a float.
Args:
data: A float or a np.double.
Returns:
A MediaPipe double Packet.
Raises:
TypeError: If the input is neither a float nore a np.double.
Examples:
packet = mp.packet_creator.create_double(0.1)
data = mp.packet_getter.get_float(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int_array",
[](const std::vector<int>& data) {
int* ints = new int[data.size()];
std::copy(data.begin(), data.end(), ints);
return Adopt(reinterpret_cast<int(*)[]>(ints));
},
R"doc(Create a MediaPipe int array Packet from a list of integers.
Args:
data: A list of integers.
Returns:
A MediaPipe int array Packet.
Raises:
TypeError: If the input is not a list of integers.
Examples:
packet = mp.packet_creator.create_int_array([1, 2, 3])
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_float_array",
[](const std::vector<float>& data) {
float* floats = new float[data.size()];
std::copy(data.begin(), data.end(), floats);
return Adopt(reinterpret_cast<float(*)[]>(floats));
},
R"doc(Create a MediaPipe float array Packet from a list of floats.
Args:
data: A list of floats.
Returns:
A MediaPipe float array Packet.
Raises:
TypeError: If the input is not a list of floats.
Examples:
packet = mp.packet_creator.create_float_array([0.1, 0.2, 0.3])
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_int_vector",
[](const std::vector<int>& data) {
return MakePacket<std::vector<int>>(data);
},
R"doc(Create a MediaPipe int vector Packet from a list of integers.
Args:
data: A list of integers.
Returns:
A MediaPipe int vector Packet.
Raises:
TypeError: If the input is not a list of integers.
Examples:
packet = mp.packet_creator.create_int_vector([1, 2, 3])
data = mp.packet_getter.get_int_vector(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_float_vector",
[](const std::vector<float>& data) {
return MakePacket<std::vector<float>>(data);
},
R"doc(Create a MediaPipe float vector Packet from a list of floats.
Args:
data: A list of floats
Returns:
A MediaPipe float vector Packet.
Raises:
TypeError: If the input is not a list of floats.
Examples:
packet = mp.packet_creator.create_float_vector([0.1, 0.2, 0.3])
data = mp.packet_getter.get_float_list(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_string_vector",
[](const std::vector<std::string>& data) {
return MakePacket<std::vector<std::string>>(data);
},
R"doc(Create a MediaPipe std::string vector Packet from a list of str.
Args:
data: A list of str.
Returns:
A MediaPipe std::string vector Packet.
Raises:
TypeError: If the input is not a list of str.
Examples:
packet = mp.packet_creator.create_string_vector(['a', 'b', 'c'])
data = mp.packet_getter.get_str_list(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_packet_vector",
[](const std::vector<Packet>& data) {
return MakePacket<std::vector<Packet>>(data);
},
R"doc(Create a MediaPipe Packet holds a vector of packets.
Args:
data: A list of packets.
Returns:
A MediaPipe Packet holds a vector of packets.
Raises:
TypeError: If the input is not a list of packets.
Examples:
packet = mp.packet_creator.create_packet_vector([
mp.packet_creator.create_float(0.1),
mp.packet_creator.create_int(1),
mp.packet_creator.create_string('1')
])
data = mp.packet_getter.get_packet_vector(packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_string_to_packet_map",
[](const std::map<std::string, Packet>& data) {
return MakePacket<std::map<std::string, Packet>>(data);
},
R"doc(Create a MediaPipe std::string to packet map Packet from a dictionary.
Args:
data: A dictionary that has (str, Packet) pairs.
Returns:
A MediaPipe Packet holds std::map<std::string, Packet>.
Raises:
TypeError: If the input is not a dictionary from str to packet.
Examples:
dict_packet = mp.packet_creator.create_string_to_packet_map({
'float': mp.packet_creator.create_float(0.1),
'int': mp.packet_creator.create_int(1),
'std::string': mp.packet_creator.create_string('1')
data = mp.packet_getter.get_str_to_packet_dict(dict_packet)
)doc",
py::arg().noconvert(), py::return_value_policy::move);
m->def(
"create_matrix",
// Eigen Map class
// (https://eigen.tuxfamily.org/dox/group__TutorialMapClass.html) is the
// way to reuse the external memory as an Eigen type. However, when
// creating an Eigen::MatrixXf from an Eigen Map object, the data copy
// still happens. We can make a packet of an Eigen Map type for reusing
// external memory. However,the packet data type is no longer
// Eigen::MatrixXf.
// TODO: Should take "const Eigen::Ref<const Eigen::MatrixXf>&"
// as the input argument. Investigate why bazel non-optimized mode
// triggers a memory allocation bug in Eigen::internal::aligned_free().
[](const Eigen::MatrixXf& matrix) {
// MakePacket copies the data.
return MakePacket<Matrix>(matrix);
},
R"doc(Create a MediaPipe Matrix Packet from a 2d numpy float ndarray.
The method copies data from the input MatrixXf and the returned packet owns
a MatrixXf object.
Args:
matrix: A 2d numpy float ndarray.
Returns:
A MediaPipe Matrix Packet.
Raises:
TypeError: If the input is not a 2d numpy float ndarray.
Examples:
packet = mp.packet_creator.create_matrix(
np.array([[.1, .2, .3], [.4, .5, .6]])
matrix = mp.packet_getter.get_matrix(packet)
)doc",
py::return_value_policy::move);
}
void InternalPacketCreators(pybind11::module* m) {
m->def(
"_create_image_frame_with_copy",
[](mediapipe::ImageFormat::Format format, const py::array& data) {
return CreateImageFramePacket(format, data);
},
py::arg("format"), py::arg("data").noconvert(),
py::return_value_policy::move);
m->def(
"_create_image_frame_with_reference",
[](mediapipe::ImageFormat::Format format, const py::array& data) {
throw RaisePyError(
PyExc_NotImplementedError,
"Creating image frame packet with reference is not supproted yet.");
},
py::arg("format"), py::arg("data").noconvert(),
py::return_value_policy::move);
m->def(
"_create_image_frame_with_copy",
[](ImageFrame& image_frame) {
auto image_frame_copy = absl::make_unique<ImageFrame>();
// Set alignment_boundary to kGlDefaultAlignmentBoundary so that
// both GPU and CPU can process it.
image_frame_copy->CopyFrom(image_frame,
ImageFrame::kGlDefaultAlignmentBoundary);
return Adopt(image_frame_copy.release());
},
py::arg("image_frame").noconvert(), py::return_value_policy::move);
m->def(
"_create_image_frame_with_reference",
[](ImageFrame& image_frame) {
throw RaisePyError(
PyExc_NotImplementedError,
"Creating image frame packet with reference is not supproted yet.");
},
py::arg("image_frame").noconvert(), py::return_value_policy::move);
m->def(
"_create_proto",
[](const std::string& type_name, const py::bytes& serialized_proto) {
using packet_internal::HolderBase;
mediapipe::StatusOr<std::unique_ptr<HolderBase>> maybe_holder =
packet_internal::MessageHolderRegistry::CreateByName(type_name);
if (!maybe_holder.ok()) {
throw RaisePyError(
PyExc_RuntimeError,
absl::StrCat("Unregistered proto message type: ", type_name)
.c_str());
}
// Creates a Packet with the concrete C++ payload type.
std::unique_ptr<HolderBase> message_holder =
std::move(maybe_holder).ValueOrDie();
auto* copy = const_cast<proto_ns::MessageLite*>(
message_holder->GetProtoMessageLite());
copy->ParseFromString(serialized_proto);
return packet_internal::Create(message_holder.release());
},
py::return_value_policy::move);
m->def(
"_create_proto_vector",
[](const std::string& type_name,
const std::vector<py::bytes>& serialized_proto_vector) {
// TODO: Implement this.
throw RaisePyError(PyExc_NotImplementedError,
"Creating a packet from a vector of proto messages "
"is not supproted yet.");
return Packet();
},
py::return_value_policy::move);
}
void PacketCreatorSubmodule(pybind11::module* module) {
py::module m = module->def_submodule(
"_packet_creator", "MediaPipe internal packet creator module.");
PublicPacketCreators(&m);
InternalPacketCreators(&m);
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_PACKET_CREATOR_H_
#define MEDIAPIPE_PYTHON_PYBIND_PACKET_CREATOR_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void PacketCreatorSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_PACKET_CREATOR_H_
+395
View File
@@ -0,0 +1,395 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/packet_getter.h"
#include "mediapipe/framework/formats/matrix.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/framework/timestamp.h"
#include "mediapipe/python/pybind/image_frame_util.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/eigen.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
namespace mediapipe {
namespace python {
namespace {
template <typename T>
const T& GetContent(const Packet& packet) {
RaisePyErrorIfNotOk(packet.ValidateAsType<T>());
return packet.Get<T>();
}
} // namespace
namespace py = pybind11;
void PublicPacketGetters(pybind11::module* m) {
m->def("get_str", &GetContent<std::string>,
R"doc(Get the content of a MediaPipe std::string Packet as a str.
Args:
packet: A MediaPipe std::string Packet.
Returns:
A str.
Raises:
ValueError: If the Packet doesn't contain std::string data.
Examples:
packet = mp.packet_creator.create_string('abc')
data = mp.packet_getter.get_str(packet)
)doc");
m->def(
"get_bytes",
[](const Packet& packet) {
return py::bytes(GetContent<std::string>(packet));
},
R"doc(Get the content of a MediaPipe std::string Packet as a bytes object.
Args:
packet: A MediaPipe std::string Packet.
Returns:
A bytes object.
Raises:
ValueError: If the Packet doesn't contain std::string data.
Examples:
packet = mp.packet_creator.create_string(b'\xd0\xd0\xd0')
data = mp.packet_getter.get_bytes(packet)
)doc");
m->def("get_bool", &GetContent<bool>,
R"doc(Get the content of a MediaPipe bool Packet as a boolean.
Args:
packet: A MediaPipe bool Packet.
Returns:
A boolean.
Raises:
ValueError: If the Packet doesn't contain bool data.
Examples:
packet = mp.packet_creator.create_bool(True)
data = mp.packet_getter.get_bool(packet)
)doc");
m->def(
"get_int",
[](const Packet& packet) {
if (packet.ValidateAsType<int>().ok()) {
return static_cast<int64>(packet.Get<int>());
} else if (packet.ValidateAsType<int8>().ok()) {
return static_cast<int64>(packet.Get<int8>());
} else if (packet.ValidateAsType<int16>().ok()) {
return static_cast<int64>(packet.Get<int16>());
} else if (packet.ValidateAsType<int32>().ok()) {
return static_cast<int64>(packet.Get<int32>());
} else if (packet.ValidateAsType<int64>().ok()) {
return static_cast<int64>(packet.Get<int64>());
}
throw RaisePyError(
PyExc_ValueError,
"Packet doesn't contain int, int8, int16, int32, or int64 data.");
},
R"doc(Get the content of a MediaPipe int Packet as an integer.
Args:
packet: A MediaPipe Packet that holds int, int8, int16, int32, or int64 data.
Returns:
An integer.
Raises:
ValueError: If the Packet doesn't contain int, int8, int16, int32, or int64 data.
Examples:
packet = mp.packet_creator.create_int(0)
data = mp.packet_getter.get_int(packet)
)doc");
m->def(
"get_uint",
[](const Packet& packet) {
if (packet.ValidateAsType<uint8>().ok()) {
return static_cast<std::uint64_t>(packet.Get<uint8>());
} else if (packet.ValidateAsType<uint16>().ok()) {
return static_cast<std::uint64_t>(packet.Get<uint16>());
} else if (packet.ValidateAsType<uint32>().ok()) {
return static_cast<std::uint64_t>(packet.Get<uint32>());
} else if (packet.ValidateAsType<uint64>().ok()) {
return static_cast<std::uint64_t>(packet.Get<uint64>());
}
throw RaisePyError(
PyExc_ValueError,
"Packet doesn't contain uint8, uint16, uint32, or uint64 data.");
},
R"doc(Get the content of a MediaPipe uint Packet as an integer.
Args:
packet: A MediaPipe Packet that holds uint8, uint16, uint32, or uint64 data.
Raises:
ValueError: If the Packet doesn't contain uint8, uint16, uint32, or uint64 data.
Returns:
An integer.
Examples:
packet = mp.packet_creator.create_uint8(2**8 - 1)
data = mp.packet_getter.get_uint(packet)
)doc");
m->def(
"get_float",
[](const Packet& packet) {
if (packet.ValidateAsType<float>().ok()) {
return packet.Get<float>();
} else if (packet.ValidateAsType<double>().ok()) {
return static_cast<float>(packet.Get<double>());
}
throw RaisePyError(PyExc_ValueError,
"Packet doesn't contain float or double data.");
},
R"doc(Get the content of a MediaPipe float or double Packet as a float.
Args:
packet: A MediaPipe Packet that holds float or double data.
Raises:
ValueError: If the Packet doesn't contain float or double data.
Returns:
A float.
Examples:
packet = mp.packet_creator.create_float(0.1)
data = mp.packet_getter.get_float(packet)
)doc");
m->def(
"get_int_list", &GetContent<std::vector<int>>,
R"doc(Get the content of a MediaPipe int vector Packet as an integer list.
Args:
packet: A MediaPipe Packet that holds std:vector<int>.
Returns:
An integer list.
Raises:
ValueError: If the Packet doesn't contain std:vector<int>.
Examples:
packet = mp.packet_creator.create_int_vector([1, 2, 3])
data = mp.packet_getter.get_int_list(packet)
)doc");
m->def(
"get_float_list", &GetContent<std::vector<float>>,
R"doc(Get the content of a MediaPipe float vector Packet as a float list.
Args:
packet: A MediaPipe Packet that holds std:vector<float>.
Returns:
A float list.
Raises:
ValueError: If the Packet doesn't contain std:vector<float>.
Examples:
packet = packet_creator.create_float_vector([0.1, 0.2, 0.3])
data = packet_getter.get_float_list(packet)
)doc");
m->def(
"get_str_list", &GetContent<std::vector<std::string>>,
R"doc(Get the content of a MediaPipe std::string vector Packet as a str list.
Args:
packet: A MediaPipe Packet that holds std:vector<std::string>.
Returns:
A str list.
Raises:
ValueError: If the Packet doesn't contain std:vector<std::string>.
Examples:
packet = mp.packet_creator.create_string_vector(['a', 'b', 'c'])
data = mp.packet_getter.get_str_list(packet)
)doc");
m->def(
"get_packet_list", &GetContent<std::vector<Packet>>,
R"doc(Get the content of a MediaPipe Packet of Packet vector as a Packet list.
Args:
packet: A MediaPipe Packet that holds std:vector<Packet>.
Returns:
A Packet list.
Raises:
ValueError: If the Packet doesn't contain std:vector<Packet>.
Examples:
packet = mp.packet_creator.create_packet_vector([
packet_creator.create_float(0.1),
packet_creator.create_int(1),
packet_creator.create_string('1')
])
packet_list = mp.packet_getter.get_packet_list(packet)
)doc");
m->def(
"get_str_to_packet_dict", &GetContent<std::map<std::string, Packet>>,
R"doc(Get the content of a MediaPipe Packet as a dictionary that has (str, Packet) pairs.
Args:
packet: A MediaPipe Packet that holds std::map<std::string, Packet>.
Returns:
A dictionary that has (str, Packet) pairs.
Raises:
ValueError: If the Packet doesn't contain std::map<std::string, Packet>.
Examples:
dict_packet = mp.packet_creator.create_string_to_packet_map({
'float': packet_creator.create_float(0.1),
'int': packet_creator.create_int(1),
'std::string': packet_creator.create_string('1')
data = mp.packet_getter.get_str_to_packet_dict(dict_packet)
)doc");
m->def(
"get_image_frame", &GetContent<ImageFrame>,
R"doc(Get the content of a MediaPipe ImageFrame Packet as an ImageFrame object.
Args:
packet: A MediaPipe ImageFrame Packet.
Returns:
A MediaPipe ImageFrame object.
Raises:
ValueError: If the Packet doesn't contain ImageFrame.
Examples:
packet = packet_creator.create_image_frame(frame)
data = packet_getter.get_image_frame(packet)
)doc",
py::return_value_policy::reference_internal);
m->def(
"get_matrix",
[](const Packet& packet) {
return Eigen::Ref<const Eigen::MatrixXf>(GetContent<Matrix>(packet));
},
R"doc(Get the content of a MediaPipe Matrix Packet as a numpy 2d float ndarray.
Args:
packet: A MediaPipe Matrix Packet.
Returns:
A numpy 2d float ndarray.
Raises:
ValueError: If the Packet doesn't contain matrix data.
Examples:
packet = mp.packet_creator.create_matrix(2d_array)
data = mp.packet_getter.get_matrix(packet)
)doc",
py::return_value_policy::reference_internal);
}
void InternalPacketGetters(pybind11::module* m) {
m->def(
"_get_proto_type_name",
[](const Packet& packet) {
return packet.GetProtoMessageLite().GetTypeName();
},
py::return_value_policy::move);
m->def(
"_get_proto_vector_size",
[](Packet& packet) {
auto proto_vector = packet.GetVectorOfProtoMessageLitePtrs();
RaisePyErrorIfNotOk(proto_vector.status());
return proto_vector.ValueOrDie().size();
},
py::return_value_policy::move);
m->def(
"_get_proto_vector_element_type_name",
[](Packet& packet) {
auto proto_vector = packet.GetVectorOfProtoMessageLitePtrs();
RaisePyErrorIfNotOk(proto_vector.status());
if (proto_vector.ValueOrDie().empty()) {
return std::string();
}
return proto_vector.ValueOrDie()[0]->GetTypeName();
},
py::return_value_policy::move);
m->def(
"_get_serialized_proto",
[](const Packet& packet) {
// By default, py::bytes is an extra copy of the original std::string
// object: https://github.com/pybind/pybind11/issues/1236 Howeover, when
// Pybind11 performs the C++ to Python transition, it only increases the
// py::bytes object's ref count. See the implmentation at line 1583 in
// "pybind11/cast.h".
return py::bytes(packet.GetProtoMessageLite().SerializeAsString());
},
py::return_value_policy::move);
m->def(
"_get_serialized_proto_list",
[](Packet& packet) {
auto proto_vector = packet.GetVectorOfProtoMessageLitePtrs();
RaisePyErrorIfNotOk(proto_vector.status());
int size = proto_vector.ValueOrDie().size();
std::vector<py::bytes> results;
results.reserve(size);
for (const proto_ns::MessageLite* ptr : proto_vector.ValueOrDie()) {
results.push_back(py::bytes(ptr->SerializeAsString()));
}
return results;
},
py::return_value_policy::move);
}
void PacketGetterSubmodule(pybind11::module* module) {
py::module m = module->def_submodule(
"_packet_getter", "MediaPipe internal packet getter module.");
PublicPacketGetters(&m);
InternalPacketGetters(&m);
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_PACKET_GETTER_H_
#define MEDIAPIPE_PYTHON_PYBIND_PACKET_GETTER_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void PacketGetterSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_PACKET_GETTER_H_
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/resource_util.h"
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
ABSL_DECLARE_FLAG(std::string, resource_root_dir);
namespace mediapipe {
namespace python {
namespace py = pybind11;
void ResourceUtilSubmodule(pybind11::module* module) {
py::module m =
module->def_submodule("resource_util", "MediaPipe resource util module.");
m.def(
"set_resource_dir",
[](const std::string& str) {
absl::SetFlag(&FLAGS_resource_root_dir, str);
},
R"doc(Set resource root directory where can find necessary graph resources such as model files and label maps.
Args:
str: A UTF-8 str.
Examples:
mp.resource_util.set_resource_dir('/path/to/resource')
)doc");
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_RESOURCE_UTIL_H_
#define MEDIAPIPE_PYTHON_PYBIND_RESOURCE_UTIL_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void ResourceUtilSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_RESOURCE_UTIL_H_
+144
View File
@@ -0,0 +1,144 @@
// Copyright 2020 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.
#include "mediapipe/python/pybind/timestamp.h"
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/timestamp.h"
#include "mediapipe/python/pybind/util.h"
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
namespace py = pybind11;
void TimestampSubmodule(pybind11::module* module) {
py::module m =
module->def_submodule("timestamp", "MediaPipe timestamp module.");
py::class_<Timestamp> timestamp(
m, "Timestamp",
R"doc(A class which represents a timestamp in the MediaPipe framework.
MediaPipe timestamps are in units of _microseconds_.
There are several special values (All these values must be constructed using
the static methods provided):
UNSET: The default initialization value, not generally valid when a
timestamp is required.
UNSTARTED: The timestamp before any valid timestamps. This is the input
timestamp during Calcultor::Open().
PRESTREAM: A value for specifying that a packet contains "header" data
that should be processed before any other timestamp. Like
poststream, if this value is sent then it must be the only
value that is sent on the stream.
MIN: The minimum range timestamp to see in Calcultor::Process().
Any number of "range" timestamp can be sent over a stream,
provided that they are sent in monotonically increasing order.
MAX: The maximum range timestamp to see in Process().
POSTSTREAM: A value for specifying that a packet pertains to the entire
stream. This "summary" timestamp occurs after all the "range"
timestamps. If this timestamp is sent on a stream, it must be
the only packet sent.
DONE: The timestamp after all valid timestamps.
This is the input timestamp during Calcultor::Close().
)doc");
timestamp.def(py::init<const Timestamp&>())
.def(py::init<int64>())
.def_property_readonly("value", &Timestamp::Value)
.def_property_readonly_static(
"UNSET", [](py::object) { return Timestamp::Unset(); })
.def_property_readonly_static(
"UNSTARTED", [](py::object) { return Timestamp::Unstarted(); })
.def_property_readonly_static(
"PRESTREAM", [](py::object) { return Timestamp::PreStream(); })
.def_property_readonly_static("MIN",
[](py::object) { return Timestamp::Min(); })
.def_property_readonly_static("MAX",
[](py::object) { return Timestamp::Max(); })
.def_property_readonly_static(
"POSTSTREAM", [](py::object) { return Timestamp::PostStream(); })
.def_property_readonly_static(
"DONE", [](py::object) { return Timestamp::Done(); })
.def("__eq__",
[](const Timestamp& a, const Timestamp& b) { return a == b; })
.def("__lt__",
[](const Timestamp& a, const Timestamp& b) { return a < b; })
.def("__gt__",
[](const Timestamp& a, const Timestamp& b) { return a > b; })
.def("__le__",
[](const Timestamp& a, const Timestamp& b) { return a <= b; })
.def("__ge__",
[](const Timestamp& a, const Timestamp& b) { return a >= b; })
.def("__repr__", [](const Timestamp& self) {
return absl::StrCat("<mediapipe.Timestamp with value: ",
TimestampValueString(self), ">");
});
timestamp.def("seconds", &Timestamp::Seconds,
R"doc(Return the value in units of seconds as a float.)doc");
timestamp.def(
"microseconds", &Timestamp::Microseconds,
R"doc(Return the value in units of microseconds as an int.)doc");
timestamp.def("is_special_value", &Timestamp::IsSpecialValue,
R"doc(Check if the timestamp is a special value,
A special value is any of the values which cannot be constructed directly
but must be constructed using the static special value.
)doc");
timestamp.def(
"is_range_value", &Timestamp::IsRangeValue,
R"doc(Check if the timestamp is a range value is anything between Min() and Max() (inclusive).
Any number of packets with range values can be sent over a stream as long as
they are sent in monotonically increasing order. is_range_value() isn't
quite the opposite of is_special_value() since it is valid to start a stream
at Timestamp::Min() and continue until timestamp max (both of which are
special values). prestream and postStream are not considered a range value
even though they can be sent over a stream (they are "summary" timestamps not
"range" timestamps).
)doc");
timestamp.def(
"is_allowed_in_stream", &Timestamp::IsAllowedInStream,
R"doc(Returns true iff this can be the timestamp of a Packet in a stream.
Any number of RangeValue timestamps may be in a stream (in monotonically
increasing order). Also, exactly one prestream, or one poststream packet is
allowed.
)doc");
timestamp.def_static("from_seconds", &Timestamp::FromSeconds,
R"doc(Create a timestamp from a seconds value
Args:
seconds: A seconds value in float.
Returns:
A MediaPipe Timestamp object.
Examples:
timestamp_now = mp.Timestamp.from_seconds(time.time())
)doc");
py::implicitly_convertible<int64, Timestamp>();
}
} // namespace python
} // namespace mediapipe
+28
View File
@@ -0,0 +1,28 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_TIMESTAMP_H_
#define MEDIAPIPE_PYTHON_PYBIND_TIMESTAMP_H_
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
void TimestampSubmodule(pybind11::module* module);
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_TIMESTAMP_H_
+92
View File
@@ -0,0 +1,92 @@
// Copyright 2020 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.
#ifndef MEDIAPIPE_PYTHON_PYBIND_UTIL_H_
#define MEDIAPIPE_PYTHON_PYBIND_UTIL_H_
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/timestamp.h"
#include "pybind11/pybind11.h"
namespace mediapipe {
namespace python {
namespace py = pybind11;
inline py::error_already_set RaisePyError(PyObject* exc_class,
const char* message) {
PyErr_SetString(exc_class, message);
return py::error_already_set();
}
inline PyObject* StatusCodeToPyError(const ::absl::StatusCode& code) {
switch (code) {
case absl::StatusCode::kInvalidArgument:
return static_cast<PyObject*>(PyExc_ValueError);
case absl::StatusCode::kAlreadyExists:
return static_cast<PyObject*>(PyExc_FileExistsError);
case absl::StatusCode::kUnimplemented:
return static_cast<PyObject*>(PyExc_NotImplementedError);
default:
return static_cast<PyObject*>(PyExc_RuntimeError);
}
}
inline void RaisePyErrorIfNotOk(const mediapipe::Status& status) {
if (!status.ok()) {
throw RaisePyError(StatusCodeToPyError(status.code()),
status.message().data());
}
}
inline void RaisePyErrorIfOverflow(int64 value, int64 min, int64 max) {
if (value > max) {
throw RaisePyError(PyExc_OverflowError,
absl::StrCat(value, " execeeds the maximum value (", max,
") the data type can have.")
.c_str());
} else if (value < min) {
throw RaisePyError(PyExc_OverflowError,
absl::StrCat(value, " goes below the minimum value (",
min, ") the data type can have.")
.c_str());
}
}
inline std::string TimestampValueString(const Timestamp& timestamp) {
if (timestamp == Timestamp::Unset()) {
return "UNSET";
} else if (timestamp == Timestamp::Unstarted()) {
return "UNSTARTED";
} else if (timestamp == Timestamp::PreStream()) {
return "PRESTREAM";
} else if (timestamp == Timestamp::Min()) {
return "MIN";
} else if (timestamp == Timestamp::Max()) {
return "MAX";
} else if (timestamp == Timestamp::PostStream()) {
return "POSTSTREAM";
} else if (timestamp == Timestamp::OneOverPostStream()) {
return "ONEOVERPOSTSTREAM";
} else if (timestamp == Timestamp::Done()) {
return "DONE";
} else {
return timestamp.DebugString();
}
}
} // namespace python
} // namespace mediapipe
#endif // MEDIAPIPE_PYTHON_PYBIND_UTIL_H_
+75
View File
@@ -0,0 +1,75 @@
# Copyright 2020 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._framework_bindings.timestamp."""
import time
from absl.testing import absltest
import mediapipe.python as mp
class TimestampTest(absltest.TestCase):
def testTimesatmp(self):
t = mp.Timestamp(100)
self.assertEqual(t.value, 100)
self.assertEqual(t, 100)
self.assertEqual(str(t), '<mediapipe.Timestamp with value: 100>')
def testTimestampCopyConstructor(self):
ts1 = mp.Timestamp(100)
ts2 = mp.Timestamp(ts1)
self.assertEqual(ts1, ts2)
def testTimesatmpComparsion(self):
ts1 = mp.Timestamp(100)
ts2 = mp.Timestamp(100)
self.assertEqual(ts1, ts2)
ts3 = mp.Timestamp(200)
self.assertNotEqual(ts1, ts3)
def testTimesatmpSpecialValues(self):
t1 = mp.Timestamp.UNSET
self.assertEqual(str(t1), '<mediapipe.Timestamp with value: UNSET>')
t2 = mp.Timestamp.UNSTARTED
self.assertEqual(str(t2), '<mediapipe.Timestamp with value: UNSTARTED>')
t3 = mp.Timestamp.PRESTREAM
self.assertEqual(str(t3), '<mediapipe.Timestamp with value: PRESTREAM>')
t4 = mp.Timestamp.MIN
self.assertEqual(str(t4), '<mediapipe.Timestamp with value: MIN>')
t5 = mp.Timestamp.MAX
self.assertEqual(str(t5), '<mediapipe.Timestamp with value: MAX>')
t6 = mp.Timestamp.POSTSTREAM
self.assertEqual(str(t6), '<mediapipe.Timestamp with value: POSTSTREAM>')
t7 = mp.Timestamp.DONE
self.assertEqual(str(t7), '<mediapipe.Timestamp with value: DONE>')
def testTimestampComparisons(self):
ts1 = mp.Timestamp(100)
ts2 = mp.Timestamp(101)
self.assertGreater(ts2, ts1)
self.assertGreaterEqual(ts2, ts1)
self.assertLess(ts1, ts2)
self.assertLessEqual(ts1, ts2)
self.assertNotEqual(ts1, ts2)
def testFromSeconds(self):
now = time.time()
ts = mp.Timestamp.from_seconds(now)
self.assertAlmostEqual(now, ts.seconds(), delta=1)
if __name__ == '__main__':
absltest.main()