Das Python-Framework von MediaPipe ermöglicht direkten Zugriff auf die Kernkomponenten MediaPipe C++ Framework wie Timestamp, Packet und CalculatorGraph Die gebrauchsfertigen Python-Lösungen die technischen Details des Frameworks und geben einfach das lesbare Modell Inferenzergebnisse an die Aufrufer zurück.
Das MediaPipe-Framework basiert der pybind11-Bibliothek. Das C++ Core-Framework wird in Python über eine C++/Python-Sprachbindung verfügbar gemacht. Im Folgenden wird davon ausgegangen, dass die Lesenden bereits über ein grundlegendes Verständnis das Framework von MediaPipe C++. Ansonsten finden Sie nützliche Informationen in Framework-Konzepte.
Paket
Das Paket ist die grundlegende Datenflusseinheit in MediaPipe. Ein Paket besteht aus einer
einen numerischen Zeitstempel und einen gemeinsamen
Zeiger auf eine unveränderliche Nutzlast. In Python wird ein
Um ein MediaPipe-Paket zu erstellen, rufen Sie eine der Methoden zur Paketerstellung in
die
mp.packet_creator
-Modul. Entsprechend kann die Paketnutzlast mit einer der
Paket-Getter-Methoden in der
mp.packet_getter
-Modul. Beachten Sie, dass die Paketnutzlast nach dem Paket unveränderlich wird
Erstellung. Daher hat die Änderung des abgerufenen Paketinhalts keine Auswirkungen
der tatsächlichen Nutzlast im Paket. Die Python API des MediaPipe-Frameworks unterstützt die
am häufigsten verwendete Datentypen von MediaPipe (z.B. ImageFrame, Matrix, Protocol
Puffer und die primitiven Datentypen) in der Kernbindung. Die umfassende
Die folgende Tabelle zeigt die Typzuordnungen zwischen dem Python- und dem C++-Datentyp.
zusammen mit dem Paketersteller und der Content Getter-Methode für jeden Datentyp.
die von der MediaPipe Python Framework API unterstützt wird.
Python-Datentyp | Datentyp C++ | Paketersteller | Content-Getter |
---|---|---|---|
bool | bool | create_bool(True) | get_bool(packet) |
int oder np.intc | int_t | create_int(1) | get_int(packet) |
int oder np.int8 | int8_t | create_int8(2**7-1) | get_int(packet) |
int oder np.int16 | int16_t | create_int16(2**15-1) | get_int(packet) |
int oder np.int32 | int32_t | create_int32(2**31-1) | get_int(packet) |
int oder np.int64 | int64_t | create_int64(2**63-1) | get_int(packet) |
int oder np.uint8 | Uint8_t | create_uint8(2**8-1) | get_uint(packet) |
int oder np.uint16 | uint16_t | create_uint16(2**16-1) | get_uint(packet) |
int oder np.uint32 | uint32_t | create_uint32(2**32-1) | get_uint(packet) |
int oder np.uint64 | uint64_t | create_uint64(2**64-1) | get_uint(packet) |
float oder np.float32 | float | create_float(1.1) | get_float(packet) |
float oder np.double | double | create_double(1.1) | get_float(packet) |
String (UTF-8) | std::string | create_string('abc') | get_str(packet) |
Byte | std::string | create_string(b'\xd0\xd0\xd0') | get_bytes(packet) |
mp.Packet | mp::Packet | create_packet(p) | get_packet(packet) |
Liste[bool] | std::vector<bool> | create_bool_vector([True, False]) | get_bool_list(packet) |
List[int] oder List[np.intc] | int[] | create_int_array([1, 2, 3]) | get_int_list(packet, size=10) |
List[int] oder List[np.intc] | std::vector<int> | create_int_vector([1, 2, 3]) | get_int_list(packet) |
List[float] oder List[np.float] | float[] | create_float_arrary([0,1; 0,2]) | get_float_list(packet, size=10) |
List[float] oder List[np.float] | std::vector<float> | create_float_vector([0.1; 0.2]) | get_float_list(packet, size=10) |
List[str] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
Liste[mp.Packet] | std::vector<mp::Packet> | create_packet_vector( [packet1, package2]) |
get_packet_list(p) |
Mapping[str, Packet] (Zuordnung [str, Packet] | 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 und 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) |
Google Proto-Nachricht | Google Proto-Nachricht | create_proto(proto) | get_proto(packet) |
Liste[Protokoll] | std::vector<Proto> | – | get_proto_list(packet) |
Es ist nicht ungewöhnlich, dass Nutzer benutzerdefinierte C++ -Klassen erstellen und diese an Grafiken und Rechnern. So lassen Sie die Verwendung der benutzerdefinierten Klassen in Python zu mit MediaPipe-Framework können Sie die Packet API im folgenden Schritten:
pybind11 schreiben Klassenbindungscode oder eine Umwandlung des benutzerdefinierten Typs für den benutzerdefinierten Typ in einer Cc-Datei.
#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(...); }
Erstellen Sie eine neue Paketersteller- und Getter-Methode des benutzerdefinierten Typs in einem separate cc-Datei.
#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
Fügen Sie zwei Bali-Build-Regeln für die benutzerdefinierte Typbindung und das neue Paket hinzu -Methoden in der Build-Datei.
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" ], )
Erstellen Sie die pybind-Erweiterungsziele (mit dem Suffix .so) von Bazel und verschieben Sie die generierten dynamischen Bibliotheken in eines der $LD_LIBRARY_PATH-Verzeichnisse.
Verwenden Sie die Bindungsmodule in 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)
Zeitstempel
Jedes Paket enthält einen Zeitstempel in Mikrosekunden. In Python
Die Packet API bietet die einfache Methode packet.at()
zum Definieren des numerischen Werts
Zeitstempel eines Pakets. Im Allgemeinen ist packet.timestamp
die Paketklasse.
für den Zugriff auf den zugrunde liegenden Zeitstempel. So konvertieren Sie eine Unix-Epoche in eine
MediaPipe-Zeitstempel
Zeitstempel-API
bietet hierfür die Methode mp.Timestamp.from_seconds()
.
ImageFrame
ImageFrame ist der Container zum Speichern eines Bild- oder Videoframes. Formate
die von ImageFrame unterstützt werden,
ImageFormat-Enum.
Pixel sind zeilenweise mit verschränkten Farbkomponenten codiert. ImageFrame
unterstützt uint8, uint16 und float als Datentypen. MediaPipe bietet
ImageFrame-Python-API
um auf die ImageFrame C++-Klasse zuzugreifen. In Python besteht die einfachste Methode zum Abrufen des
Pixeldaten besteht darin, image_frame.numpy_view()
aufzurufen, um ein NumPy und Array zu erhalten. Hinweis
dass das zurückgegebene numpy ndarray, ein Verweis auf die internen Pixeldaten,
unbeschreibbar. Wenn die Aufrufer das numpy ndarray ändern müssen,
explizit einen Kopiervorgang auf, um eine Kopie zu erhalten. Wenn MediaPipe einen NumPy annimmt
ndarray , um einen ImageFrame zu erstellen, geht es davon aus, dass die Daten zusammenhängend gespeichert sind.
Entsprechend werden die Pixeldaten eines ImageFrames
wenn sie an die Python-Seite zurückgegeben werden.
Grafik
Im MediaPipe Framework erfolgt die gesamte Verarbeitung im Kontext eines CalculatorGraph Die CalculatorGraph Python API ist eine direkte Bindung an die C++ CalculatorGraph-Klasse. Der größte Unterschied das CalculatorGraph Python-API einen Python-Fehler ausgibt, anstatt einen nicht-OK-Status, wenn ein Fehler auftritt. Als Python-Nutzer können Sie wie Sie es normalerweise tun. Der Lebenszyklus eines CalculatorGraph enthält drei Phasen: Initialisierung und Einrichtung, Ausführung der Grafik und Deaktivierung.
RechnerGraph mit einem RechnerGraphConfig-Protokollzwischenspeicher oder Binärprogramm initialisieren protobuf-Datei und stellen Sie Callback-Methoden zur Beobachtung der Ausgabe bereit. Streams.
Option 1: RechnerGraph mit einem CalculatorGraphConfig-protobuf initialisieren oder dessen Textdarstellung und beobachten Sie den oder die Ausgabestreams:
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: Initialisieren Sie einen CalculatorGraph mit einer binären protobuf-Datei und beobachten Sie den oder die Ausgabestreams.
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}'))
Starten Sie die Grafikausführung und fügen Sie Pakete in das Diagramm ein.
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))
Grafik nach Abschluss schließen. Sie können die Grafik für eine andere Grafik neu starten. nach dem Aufruf von
close()
ausgeführt werden.graph.close()
Das Python-Skript kann von Ihrer lokalen Python-Laufzeit ausgeführt werden.