El framework de MediaPipe Python otorga acceso directo a los componentes centrales de el framework de MediaPipe C++, como Timestamp, Package y CalculatorGraph, mientras que las soluciones de Python listas para usar los detalles técnicos del framework y solo devolver el modelo legible los resultados de la inferencia a los llamadores.
El framework de MediaPipe se apoya en la biblioteca pybind11. El framework principal de C++ se expone en Python a través de una vinculación de lenguaje C++/Python. El siguiente contenido da por sentado que el lector ya tiene un conocimiento básico de el framework de C++ de MediaPipe. De lo contrario, puedes encontrar información útil en Conceptos del framework.
Paquete
El paquete es la unidad básica de flujo de datos en MediaPipe. Un paquete consta de un
y un puntero compartido a una carga útil inmutable. En Python, un
Un paquete MediaPipe se puede crear llamando a uno de los métodos de creación de paquetes en
el
mp.packet_creator
módulo. En consecuencia, la carga útil del paquete se puede recuperar usando una de las
métodos get de paquetes en la
mp.packet_getter
módulo. Ten en cuenta que la carga útil del paquete se vuelve inmutable después de que el paquete
de la creación de cuentas de servicio. Por lo tanto, la modificación del contenido del paquete recuperado no afecta
la carga útil real en el paquete. La API de Python del framework MediaPipe admite las
los tipos de datos de MediaPipe más utilizados (p.ej., ImageFrame, matriz, protocolo
búferes y tipos de datos primitivos) en la vinculación principal. La solución integral
En la siguiente tabla, se muestran las asignaciones de tipos entre los tipos de datos de Python y C++.
junto con el creador de paquetes y el método get de contenido para cada tipo de datos
compatibles con la API del framework de MediaPipe para Python.
Tipo de datos de Python | Tipo de datos de C++ | Creador de paquetes | Método get de contenido |
---|---|---|---|
bool | bool | create_bool(True) | get_bool(packet) |
int o np.intc | int_t | create_int(1) | get_int(packet) |
int o np.int8 | int8_t | create_int8(2**7-1) | get_int(packet) |
int o np.int16 | int16_t | create_int16(2**15-1) | get_int(packet) |
int o np.int32 | int32_t | create_int32(2**31-1) | get_int(packet) |
int o np.int64 | int64_t | create_int64(2**63-1) | get_int(packet) |
int o np.uint8 | uint8_t | create_uint8(2**8-1) | get_uint(packet) |
int o np.uint16 | uint16_t | create_uint16(2**16-1) | get_uint(packet) |
int o np.uint32 | uint32_t | create_uint32(2**32-1) | get_uint(packet) |
int o np.uint64 | uint64_t | create_uint64(2**64-1) | get_uint(packet) |
float o np.float32 | float | create_float(1.1) | get_float(packet) |
float o np.double | doble | create_double(1.1) | get_float(packet) |
str (UTF-8) | std::cadena | create_string('abc') | get_str(packet) |
bytes | std::cadena | create_string(b'\xd0\xd0\xd0') | get_bytes(packet) |
mp.Packet | mp::Packet | create_packet(p) | get_packet(packet) |
Lista[bool] | std::vector<bool> | create_bool_vector([Verdadero, Falso]) | get_bool_list(packet) |
List[int] o List[np.intc] | int[] | create_int_array([1, 2, 3]) | get_int_list(paquete, tamaño=10) |
List[int] o List[np.intc] | std::vector<int> | create_int_vector([1, 2, 3]) | get_int_list(packet) |
List[float] o List[np.float] | float[] | create_float_arrary([0.1, 0.2]) | get_float_list(paquete, tamaño=10) |
List[float] o List[np.float] | std::vector<float> | create_float_vector([0.1, 0.2]) | get_float_list(paquete, tamaño=10) |
List[str] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
Lista[mp.Paquete] | std::vector<mp::Packet> | create_packet_vector( [paquete1, paquete2]) |
get_packet_list(p) |
Asignación[str, paquete] | std::map<std::string, package=""></std::string,> | create_string_to_packet_map( {'a': packet1, 'b': packet2}) |
get_str_to_packet_dict(packet) |
np.ndarray (cv.mat y PIL.Image) |
mp::ImageFrame | create_image_frame( format=ImageFormat.SRGB, data=mat) |
get_image_frame(packet) |
np.ndarray | mp::Matrix | create_matrix(data) | get_matrix(packet) |
Mensaje de protocolo de Google | Mensaje de protocolo de Google | create_proto(proto) | get_proto(packet) |
Lista[Proto] | std::vector<Proto> | N/A | get_proto_list(packet) |
Es común que los usuarios creen clases de C++ personalizadas y las envíen a los gráficos y las calculadoras. Para permitir que las clases personalizadas se usen en Python con el framework de MediaPipe, puedes extender la API de paquetes para un nuevo tipo de datos en la los siguientes pasos:
Escribe el pybind11 código de vinculación de clase o una conversión de tipos personalizada para el tipo personalizado en un archivo cc.
#include "path/to/my_type/header/file.h" #include "pybind11/pybind11.h" namespace py = pybind11; PYBIND11_MODULE(my_type_binding, m) { // Write binding code or a custom type caster for MyType. py::class_<MyType>(m, "MyType") .def(py::init<>()) .def(...); }
Crearás un nuevo método get y creador de paquetes del tipo personalizado en un archivo cc separado.
#include "path/to/my_type/header/file.h" #include "mediapipe/framework/packet.h" #include "pybind11/pybind11.h" namespace mediapipe { namespace py = pybind11; PYBIND11_MODULE(my_packet_methods, m) { m.def( "create_my_type", [](const MyType& my_type) { return MakePacket<MyType>(my_type); }); m.def( "get_my_type", [](const Packet& packet) { if(!packet.ValidateAsType<MyType>().ok()) { PyErr_SetString(PyExc_ValueError, "Packet data type mismatch."); return py::error_already_set(); } return packet.Get<MyType>(); }); } } // namespace mediapipe
Agrega dos reglas de compilación de Bazel para la vinculación de tipo personalizado y el paquete nuevo. en el archivo BUILD.
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") pybind_extension( name = "my_type_binding", srcs = ["my_type_binding.cc"], deps = [":my_type"], ) pybind_extension( name = "my_packet_methods", srcs = ["my_packet_methods.cc"], deps = [ ":my_type", "//mediapipe/framework:packet" ], )
Compila los destinos de extensión pybind (con el sufijo .so) de Bazel y mueve las bibliotecas dinámicas generadas a uno de los dirs $LD_LIBRARY_PATH.
Usar los módulos de vinculación en Python
import my_type_binding import my_packet_methods packet = my_packet_methods.create_my_type(my_type_binding.MyType()) my_type = my_packet_methods.get_my_type(packet)
Marca de tiempo
Cada paquete contiene una marca de tiempo que está en unidades de microsegundos. En Python,
La API de Packaged proporciona un método de conveniencia packet.at()
para definir el valor numérico
marca de tiempo de un paquete. En términos más generales, packet.timestamp
es la clase del paquete.
para acceder a la marca de tiempo subyacente. Para convertir un ciclo de entrenamiento Unix en
Marca de tiempo de MediaPipe,
la API de Timestamp
ofrece un método mp.Timestamp.from_seconds()
para este fin.
ImageFrame
ImageFrame es el contenedor para almacenar una imagen o un fotograma de video. Formatos
compatibles con ImageFrame se enumeran en
la enumeración ImageFormat.
Los píxeles están codificados en filas principales con componentes de color intercalados, y ImageFrame
admite uint8, uint16 y float como sus tipos de datos. MediaPipe proporciona
una API de ImageFrame para Python
para acceder a la clase C++ de ImageFrame. En Python, la forma más fácil de recuperar el
datos de píxeles es llamar a image_frame.numpy_view()
para obtener un ndarray de NumPy. Nota
que se muestra ndarray de NumPy, una referencia a los datos de píxeles internos,
que no se pueda escribir. Si los llamadores necesitan modificar el ndarray de NumPy, es necesario
llamar de manera explícita a una operación de copia para obtener una copia. Cuando MediaPipe toma un archivo
ndarray para crear un ImageFrame, este supone que los datos se almacenan de forma contigua.
En consecuencia, los datos de píxeles de un ImageFrame se volverán a alinear para que
es contiguo cuando regresa al lado de Python.
Gráfico
En el marco de trabajo de MediaPipe, todo el procesamiento tiene lugar en el contexto de un CalculatorGraph. La API de CalculatorGraph para Python es una vinculación directa con la clase C++ CalculatorGraph. La principal diferencia es la API de CalculatorGraph para Python genera un error de Python en lugar de mostrar un un estado de no OK cuando se produce un error. Por lo tanto, como usuario de Python, puedes controlar las excepciones como lo haces normalmente. El ciclo de vida de un CalculatorGraph contiene tres etapas: inicialización y configuración, ejecución del grafo y cierre del grafo.
Cómo inicializar un CalculatorGraph con un objeto binario o protobuf de CalculatorGraphConfig archivo protobuf y proporcionar métodos de devolución de llamada para observar el resultado transmisiones.
Opción 1: Cómo inicializar un CalculatorGraph con un protobuf de CalculatorGraphConfig o su representación de texto, y observa los flujos de salida:
import mediapipe as mp config_text = """ input_stream: 'in_stream' output_stream: 'out_stream' node { calculator: 'PassThroughCalculator' input_stream: 'in_stream' output_stream: 'out_stream' } """ graph = mp.CalculatorGraph(graph_config=config_text) output_packets = [] graph.observe_output_stream( 'out_stream', lambda stream_name, packet: output_packets.append(mp.packet_getter.get_str(packet)))
Opción 2. Inicializar un CalculatorGraph con un archivo binario protobuf observar los flujos de salida.
import mediapipe as mp # resources dependency graph = mp.CalculatorGraph( binary_graph=os.path.join( resources.GetRunfilesDir(), 'path/to/your/graph.binarypb')) graph.observe_output_stream( 'out_stream', lambda stream_name, packet: print(f'Get {packet} from {stream_name}'))
Inicia la ejecución del grafo y envía paquetes al grafo.
graph.start_run() graph.add_packet_to_input_stream( 'in_stream', mp.packet_creator.create_string('abc').at(0)) rgb_img = cv2.cvtColor(cv2.imread('/path/to/your/image.png'), cv2.COLOR_BGR2RGB) graph.add_packet_to_input_stream( 'in_stream', mp.packet_creator.create_image_frame(image_format=mp.ImageFormat.SRGB, data=rgb_img).at(1))
Cierra el gráfico después de finalizar. Puedes reiniciar el gráfico para otro gráfico se ejecuta después de la llamada a
close()
.graph.close()
Tu entorno de ejecución local de Python puede ejecutar la secuencia de comandos de Python.