Framework MediaPipe in Python

Il framework Python di MediaPipe garantisce l'accesso diretto ai componenti principali del il framework C++ MediaPipe, come Timestamp, Packet e CalculatorGraph, mentre le soluzioni Python pronte all'uso nascondono i dettagli tecnici del framework e restituiscono semplicemente il modello leggibile i risultati dell'inferenza ai chiamanti.

Il framework MediaPipe si basa su la libreria pybind11. Il framework di base C++ è esposto in Python tramite un'associazione di linguaggio C++/Python. I contenuti di seguito presuppongono che il lettore abbia già una conoscenza di base il framework MediaPipe C++. In caso contrario, puoi trovare informazioni utili in Concetti del framework.

Pacchetto

Il pacchetto è l'unità di flusso di dati di base in MediaPipe. Un pacchetto è composto da un timestamp numerico e un puntatore condiviso a un payload immutabile. In Python, Il pacchetto MediaPipe può essere creato chiamando uno dei metodi del creatore di pacchetti il mp.packet_creator in maggior dettaglio più avanti in questo modulo. Di conseguenza, il payload dei pacchetti può essere recuperato utilizzando uno dei i metodi getter di pacchetti mp.packet_getter in maggior dettaglio più avanti in questo modulo. Tieni presente che il payload del pacchetto diventa immutabile dopo il pacchetto per la creazione di contenuti. Di conseguenza, la modifica del contenuto del pacchetto recuperato non influisce il payload effettivo nel pacchetto. L'API Python del framework MediaPipe supporta i tipi di dati di MediaPipe più utilizzati (ad es. ImageFrame, Matrice, Protocollo buffer e tipi di dati primitivi) nell'associazione principale. L'esaustivo seguente mostra le mappature dei tipi tra il tipo di dati Python e C++ insieme al creatore del pacchetto e al metodo getter del contenuto per ogni tipo di dati supportate dall'API del framework Python MediaPipe.

Tipo di dati Python Tipo di dati C++ Creatore pacchetti Raccoglitore di contenuti
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)
numero in virgola mobile o np.float32 numero in virgola mobile create_float(1.1) get_float(packet)
numero in virgola mobile o np.double double create_double(1.1) get_float(packet)
str (UTF-8) std::stringa create_string('abc') get_str(packet)
byte std::stringa crea_stringa(b'\xd0\xd0\xd0') get_bytes(packet)
mp.Packet mp::Packet create_packet(p) get_packet(packet)
Elenco[bool] std::vector<bool> create_bool_vector([Vero, Falso]) get_bool_list(packet)
List[int] o List[np.intc] int[] create_int_array([1; 2; 3]) get_int_list(pacchetto; dimensione=10)
List[int] o List[np.intc] std::vector<int> create_int_vector([1; 2; 3]) get_int_list(packet)
Elenco[float] o Elenco[np.float] float[] create_float_arrary([0.1; 0.2]) get_float_list(pacchetto; dimensione=10)
Elenco[float] o Elenco[np.float] std::vector&lt;float&gt; create_float_vector([0,1; 0,2]) get_float_list(pacchetto; dimensione=10)
Elenco[str] std::vector&lt;std::string&gt; crea_vettore_stringa(['a']) get_str_list(packet)
Elenco[mp.Packet] std::vector&lt;mp::Packet&gt; create_packet_vector(
[pacchetto1, pacchetto2])
get_packet_list(p)
Mappatura[str, Packet] std::map<std::string, package=""></std::string,> create_string_to_packet_map(
        {&#39;a&#39;: packet1, &#39;b&#39;: packet2})
get_str_to_packet_dict(packet)
np.ndarray
(cv.mat e 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)
Messaggio Google Proto Messaggio Google Proto create_proto(proto) get_proto(packet)
Elenco[protocollo] std::vector<Proto> n/d get_proto_list(packet)

Non è raro che gli utenti creino classi C++ personalizzate e le inviino a grafici e calcolatrici. per consentire l'utilizzo delle classi personalizzate in Python con il framework MediaPipe, puoi estendere l'API Packet per un nuovo tipo di dati nel seguenti passaggi:

  1. Scrivi il file pybind11 codice di associazione della classe o un caster di tipo personalizzato per il tipo personalizzato in un file 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(...);
    }
    
  2. crea un nuovo metodo di creazione di pacchetti e getter di tipo personalizzato in un in un file Cc separato.

    #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
    
  3. Aggiungi due regole di build Bazel per l'associazione dei tipi personalizzati e il nuovo pacchetto nel file 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"
        ],
    )
    
  4. Crea le destinazioni dell'estensione pybind (con il suffisso .so) da Bazel e sposta le librerie dinamiche generate in una delle directory $LD_LIBRARY_PATH.

  5. Utilizzare i moduli di associazione 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)
    

Timestamp

Ogni pacchetto contiene un timestamp espresso in microsecondi. In Python, l'API Packet fornisce un metodo di convenienza packet.at() per definire il numero di un pacchetto. Più in generale, packet.timestamp è la classe del pacchetto per accedere al timestamp sottostante. Per convertire un'epoca Unix in un Timestamp di MediaPipe, l'API Timestamp offre a questo scopo un metodo mp.Timestamp.from_seconds().

ImageFrame

ImageFrame è il contenitore per l'archiviazione di un'immagine o di un frame video. Formati supportati da ImageFrame sono elencati in l'enumerazione ImageFormat. I pixel sono codificati in prima riga con componenti di colore interleato e ImageFrame supporta uint8, uint16 e float come tipi di dati. MediaPipe fornisce Un'API ImageFrame Python per accedere alla classe C++ di ImageFrame. In Python, il modo più semplice per recuperare i dati pixel devono chiamare image_frame.numpy_view() per ottenere un ndarray numpy. Nota che il ndarray numpy restituito, un riferimento ai dati dei pixel interni, è non scrivibile. Se i chiamanti devono modificare il ndarray numpy, è necessario di richiamare esplicitamente un'operazione di copia per ottenerne una. Quando MediaPipe accetta un numero ndarray per creare un ImageFrame, presuppone che i dati siano memorizzati in modo contiguo. Di conseguenza, i dati dei pixel di un ImageFrame saranno riallineati contigua quando viene restituita al lato Python.

Grafico

Nel framework MediaPipe, tutta l'elaborazione avviene nel contesto di una Calcolatrice grafica. L'API Python CalculatorGraph è un'associazione diretta alla classe CalculatorGraph di C++. La differenza principale è che l'API Python CalculatorGraph genera un errore Python invece di restituire un non OK quando si verifica un errore. Di conseguenza, come utente Python, puoi gestire le eccezioni come fai normalmente. Il ciclo di vita di un grafico di Calcolatrice contiene tre fasi: inizializzazione e configurazione, esecuzione del grafico e chiusura del grafico.

  1. Inizializzare un CalculatorGraph con un protobuf o un file binario CalculatorGraphConfig protobuf e fornire metodi di callback per osservare l'output flussi.

    Opzione 1. Inizializzare un CalculatorGraph con un protobuf CalculatorGraphConfig o la sua rappresentazione testuale e osserva i flussi di output:

    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)))
    

    Opzione 2. Inizializzare un CalculatorGraph con un file binario protobuf e osserva i flussi di output.

    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}'))
    
  2. Avvia l'esecuzione del grafico e inserisci i pacchetti nel grafico.

    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))
    
  3. Al termine, chiudi il grafico. Puoi riavviare il grafico per visualizzarne un altro dopo la chiamata a close().

    graph.close()
    

Lo script Python può essere eseguito dal tuo runtime Python locale.