Le framework Python MediaPipe offre un accès direct aux principaux composants le framework MediaPipe C++ comme Timestamp, Packet et CalculatorGraph, tandis que les solutions Python prêtes à l'emploi masquent les détails techniques du framework et renvoie simplement le modèle lisible les résultats d'inférence aux appelants.
Le framework MediaPipe s'appuie la bibliothèque pybind11. Le framework de base C++ est exposé en Python via une liaison de langage C++/Python. Le contenu ci-dessous suppose que le lecteur possède déjà des connaissances de base en le framework C++ MediaPipe. Sinon, vous trouverez des informations utiles dans Concepts de framework.
Paquet
Le paquet est l'unité de flux de données de base dans MediaPipe. Un paquet est constitué d'un
un horodatage numérique et un pointeur partagé
vers une charge utile immuable. En Python,
Le paquet MediaPipe peut être créé en appelant l'une des méthodes de création de paquets dans
la
mp.packet_creator
de ce module. En conséquence, la charge utile du paquet peut être récupérée en utilisant l'une des
méthodes getter de paquets dans
mp.packet_getter
de ce module. Notez que la charge utile du paquet devient immuable après le paquet.
création. Ainsi, la modification du contenu des paquets récupérés n'affecte pas
la charge utile réelle du paquet. L'API Python du framework MediaPipe est compatible avec
types de données les plus couramment utilisés dans MediaPipe (par exemple, ImageFrame, Matrix, Protocol
les tampons et les types de données primitifs) dans la liaison de base. Fonctionnalité complète
Le tableau ci-dessous présente les mappages de types entre les types de données Python et C++
ainsi que le créateur de paquets et la méthode
getter de contenu pour chaque type de données
compatibles avec l'API MediaPipe du framework Python.
Type de données Python | Type de données C++ | Créateur de paquets | Getter de contenu |
---|---|---|---|
Bool | Bool | create_bool(True) | get_bool(packet) |
int ou np.intc | int_t | create_int(1) | get_int(packet) |
int ou np.int8 | int8_t | create_int8(2**7-1) | get_int(packet) |
int ou np.int16 | int16_t | create_int16(2**15-1) | get_int(packet) |
int ou np.int32 | int32_t | create_int32(2**31-1) | get_int(packet) |
int ou np.int64 | int64_t | create_int64(2**63-1) | get_int(packet) |
int ou np.uint8 | uint8_t | create_uint8(2**8-1) | get_uint(packet) |
int ou np.uint16 | uint16_t | create_uint16(2**16-1) | get_uint(packet) |
int ou np.uint32 | uint32_t | create_uint32(2**32-1) | get_uint(packet) |
int ou np.uint64 | uint64_t | create_uint64(2**64-1) | get_uint(packet) |
float ou np.float32 | float | create_float(1.1) | get_float(packet) |
float ou np.double | double | create_double(1.1) | get_float(packet) |
str (UTF-8) | std::chaîne | create_string('abc') | get_str(packet) |
bytes | std::chaîne | create_string(b'\xd0\xd0\xd0') | get_bytes(packet) |
mp.Packet | mp::Packet | create_packet(p) | get_packet(packet) |
Liste | std::vecteur<bool> | create_bool_vector([Vrai, Faux]) | get_bool_list(packet) |
List[int] ou List[np.intc] | entier[] | create_int_array([1, 2, 3]) | get_int_list(paquet, taille=10) |
List[int] ou List[np.intc] | std::vecteur<int> | create_int_vector([1, 2, 3]) | get_int_list(packet) |
List[float] ou List[np.float] | float[] | create_float_arrary([0,1, 0,2]) | get_float_list(paquet, taille=10) |
List[float] ou List[np.float] | std::vector<float> | create_float_vector([0,1, 0,2]) | get_float_list(paquet, taille=10) |
Liste[chaîne] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
Liste[mp.Packet] | std::vector<mp::Packet> | create_packet_vector( [paquet1, paquet2]) |
get_packet_list(p) |
Mapping[str, Packet] (Mise en correspondance) | std::map<std::string, Packet=""></std::string,> | create_string_to_packet_map( {'a': packet1, 'b': packet2}) |
get_str_to_packet_dict(packet) |
np.ndarray (cv.mat et 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) |
Message Google Proto | Message Google Proto | create_proto(proto) | get_proto(packet) |
Liste[Proto] | std::vecteur<Proto> | n/a | get_proto_list(packet) |
Il n'est pas rare que les utilisateurs créent des classes C++ personnalisées et les envoient les graphiques et les calculatrices. Pour autoriser l'utilisation des classes personnalisées dans Python avec MediaPipe Framework, vous pouvez étendre l'API Paquet pour un nouveau type de données dans le procédez comme suit:
Écrire la commande pybind11 code de liaison de classe ou une machine de roulement de caractères personnalisée pour le type personnalisé dans un fichier 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(...); }
Créez un créateur de paquets et une méthode getter du type personnalisé dans un fichier cc distinct.
#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
Ajouter deux règles de compilation Bazel pour la liaison de type personnalisée et le nouveau paquet dans le fichier CREATE.
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" ], )
Créez les cibles d'extension pybind (avec le suffixe .so) par Bazel et déplacez les bibliothèques dynamiques générées dans l'un des répertoires $LD_LIBRARY_PATH.
Utilisez les modules de liaison 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)
Horodatage
Chaque paquet contient un code temporel exprimé en microsecondes. Dans Python,
L'API Paquet fournit une méthode pratique, packet.at()
, qui permet de définir les valeurs
le code temporel d'un paquet. Plus généralement, packet.timestamp
est la classe des paquets
permettant d'accéder à l'horodatage sous-jacent. Pour convertir une époque Unix en
Code temporel MediaPipe
l'API Timestamp
propose une méthode mp.Timestamp.from_seconds()
à cet effet.
ImageFrame
ImageFrame est le conteneur permettant de stocker une image ou une image vidéo. Formats
pris en charge par ImageFrame sont répertoriés dans
Énumération ImageFormat.
Les pixels sont encodés "ligne principale" avec des composants de couleur entrelacés et ImageFrame.
prend en charge uint8, uint16 et float comme types de données. MediaPipe propose
une API Python ImageFrame
pour accéder à la classe C++ ImageFrame. En Python, le moyen le plus simple de récupérer
les données de pixel consiste à appeler image_frame.numpy_view()
pour obtenir un tableau Numpy. Remarque
que le numpy ndarray renvoyé, une référence aux données de pixels internes, est
non accessible en écriture. Si les appelants doivent modifier le ndarray numpy, il est nécessaire de
appeler explicitement une opération de copie pour obtenir une copie. Quand MediaPipe prend une requête Numpy
ndarray pour créer un ImageFrame, il suppose que les données sont stockées contiguës.
En conséquence, les données de pixel
d'un ImageFrame seront réalignées pour être
contiguës lorsqu'elles sont renvoyées du côté Python.
Graphique
Dans le framework MediaPipe, tout le traitement a lieu dans le contexte CalculatorGraph. API Python CalculatorGraph est une liaison directe vers la classe C++ CalculatorGraph. La principale différence réside dans L'API Python CalculatorGraph génère une erreur Python au lieu de renvoyer une un état non OK en cas d'erreur. Par conséquent, en tant qu'utilisateur Python, les exceptions comme vous le faites habituellement. Le cycle de vie d'un CalculatorGraph contient trois étapes: l'initialisation et la configuration, l'exécution du graphique et l'arrêt du graphique.
Initialiser un CalculatorGraph avec un protobuf ou un binaire de CalculatorGraphConfig fichier protobuf et fournir une ou plusieurs méthodes de rappel pour observer le résultat flux.
Option 1. Initialiser un CalculatorGraph avec un protobuf CalculatorGraphConfig ou sa représentation textuelle, et observez le ou les flux de sortie :
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)))
Option 2. Initialiser un CalculatorGraph avec un fichier protobuf binaire observer le ou les flux de sortie.
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}'))
Lancez l'exécution du graphique et ajoutez-y les paquets.
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))
Fermez le graphique une fois l'opération terminée. Vous pouvez relancer le graphique pour en voir un autre. s'exécute après l'appel à
close()
.graph.close()
Le script Python peut être exécuté par votre environnement d'exécution Python local.