Framework MediaPipe di Python

Kerangka kerja Python MediaPipe memberikan akses langsung ke komponen inti dari framework C++ MediaPipe seperti Timestamp, Packet, dan CalculatorGraph, sedangkan solusi Python siap digunakan menyembunyikan detail teknis kerangka kerja dan cukup mengembalikan model yang dapat dibaca hasil inferensi kembali ke pemanggil.

Kerangka kerja MediaPipe berada di atas library pybind11. Framework inti C++ diekspos di Python melalui binding bahasa C++/Python. Konten di bawah ini mengasumsikan bahwa pembaca sudah memiliki pemahaman dasar tentang framework C++ MediaPipe. Jika tidak, Anda dapat menemukan informasi yang berguna di Konsep Framework.

Paket

Paket tersebut adalah unit aliran data dasar di MediaPipe. Sebuah paket terdiri dari sebuah stempel waktu numerik dan pointer bersama ke payload yang tidak dapat diubah. Di Python, Paket MediaPipe dapat dibuat dengan memanggil salah satu metode pembuat paket di tindakan mp.packet_creator ruang lingkup modul ini. Sejalan dengan itu, {i>payload<i} paket dapat diambil dengan menggunakan salah satu metode pengambil paket di mp.packet_getter ruang lingkup modul ini. Perlu diperhatikan bahwa payload paket menjadi tidak dapat diubah setelah paket pembuatan konten. Dengan demikian, modifikasi pada isi paket yang diambil tidak mempengaruhi {i>payload<i} sebenarnya dalam paket. Framework MediaPipe Python API mendukung jenis data MediaPipe yang paling umum digunakan (misalnya, ImageFrame, Matriks, Protokol Buffer, dan jenis data primitif) dalam binding inti. Referensi yang komprehensif di bawah ini menunjukkan pemetaan tipe antara Python dan tipe data C++ beserta pembuat paket dan metode pengambil konten untuk setiap jenis data didukung oleh API framework Python MediaPipe.

Jenis Data Python Jenis Data C++ Pembuat Paket Pengambil Konten
bool bool create_bool(True) get_bool(packet)
int atau np.intc int_t create_int(1) get_int(packet)
int atau np.int8 int8_t create_int8(2**7-1) get_int(packet)
int atau np.int16 int16_t create_int16(2**15-1) get_int(packet)
int atau np.int32 int32_t create_int32(2**31-1) get_int(packet)
int atau np.int64 int64_t create_int64(2**63-1) get_int(packet)
int atau np.uint8 uint8_t create_uint8(2**8-1) get_uint(packet)
int atau np.uint16 uint16_t create_uint16(2**16-1) get_uint(packet)
int atau np.uint32 uint32_t create_uint32(2**32-1) get_uint(packet)
int atau np.uint64 uint64_t create_uint64(2**64-1) get_uint(packet)
float atau np.float32 float create_float(1.1) get_float(packet)
{i>float<i} atau np.double double create_double(1.1) get_float(packet)
str (UTF-8) {i>std::string<i} create_string('abc') get_str(packet)
byte {i>std::string<i} create_string(b'\xd0\xd0\xd0') get_bytes(packet)
mp.Packet mp::Packet create_packet(p) get_packet(packet)
Daftar[bool] std::vector<bool> create_bool_vector([Benar, Salah]) get_bool_list(packet)
List[int] atau List[np.intc] int[] create_int_array([1, 2, 3]) get_int_list(paket, ukuran=10)
List[int] atau List[np.intc] std::vector<int> create_int_vector([1, 2, 3]) get_int_list(packet)
List[float] atau List[np.float] float[] create_float_arrary([0,1, 0,2]) get_float_list(paket, ukuran=10)
List[float] atau List[np.float] std::vector&lt;float&gt; create_float_vector([0,1, 0,2]) get_float_list(paket, ukuran=10)
List[str] std::vector&lt;std::string&gt; create_string_vector(['a']) get_str_list(packet)
Daftar[mp.Packet] std::vector&lt;mp::Packet&gt; create_packet_vector(
[paket1, paket2])
get_packet_list(p)
Pemetaan[str, Paket] 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 dan 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)
Pesan Proto Google Pesan Proto Google create_proto(proto) get_proto(packet)
Daftar[Proto] std::vector<Proto> t/a get_proto_list(packet)

Tidak jarang pengguna membuat class C++ kustom dan mengirimkannya ke grafik dan kalkulator. Untuk mengizinkan class kustom digunakan di Python dengan MediaPipe Framework, Anda dapat memperluas Packet API untuk jenis data baru langkah-langkah berikut:

  1. Menulis pybind11 kode binding class atau kastor jenis kustom untuk jenis kustom dalam 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. Membuat pembuat paket dan metode pengambil baru dari jenis kustom di file cc terpisah.

    #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. Tambahkan dua aturan build bazel untuk binding jenis kustom dan paket baru dalam 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. Bangun target ekstensi pybind (dengan akhiran .so) oleh Bazel dan pindahkan library dinamis yang dihasilkan ke salah satu direktori $LD_LIBRARY_PATH.

  5. Menggunakan modul binding di 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)
    

Stempel waktu

Setiap paket berisi stempel waktu dalam satuan mikrodetik. Di Python, Packet API menyediakan metode praktis packet.at() untuk mendefinisikan stempel waktu paket. Secara lebih umum, packet.timestamp adalah class paket untuk mengakses stempel waktu yang mendasarinya. Untuk mengonversi epoch Unix ke Stempel waktu MediaPipe, Stempel Waktu API menawarkan metode mp.Timestamp.from_seconds() untuk tujuan ini.

ImageFrame

ImageFrame adalah penampung untuk menyimpan gambar atau bingkai video. Format yang didukung oleh {i>ImageFrame<i} dicantumkan di enum ImageFormat. Piksel dienkode sebagai baris utama dengan komponen warna berselang-seling, dan ImageFrame mendukung uint8, uint16, dan float sebagai jenis datanya. MediaPipe menyediakan ImageFrame Python API untuk mengakses kelas C++ ImageFrame. Di Python, cara termudah untuk mengambil data piksel akan memanggil image_frame.numpy_view() untuk mendapatkan ndarray numpy. Catatan bahwa numpy ndarray yang dikembalikan, yang merupakan referensi ke data piksel internal, tidak dapat ditulis. Jika pemanggil perlu memodifikasi numpy ndarray, Anda harus secara eksplisit memanggil operasi penyalinan untuk mendapatkan salinan. Saat MediaPipe mengambil numpy ndarray untuk membuat ImageFrame, ini mengasumsikan bahwa data disimpan secara kontinu. Dengan demikian, data piksel ImageFrame akan diselaraskan kembali agar berdekatan ketika dikembalikan ke sisi Python.

Grafik

Dalam Kerangka Kerja MediaPipe, semua pemrosesan terjadi dalam konteks CalculatorGraph. CalculatorGraph Python API adalah pengikatan langsung ke class C++ CalculatorGraph. Perbedaan utamanya adalah CalculatorGraph Python API memunculkan error Python, bukan menampilkan {i>non-OK Status <i}ketika terjadi kesalahan. Oleh karena itu, sebagai pengguna Python, Anda dapat menangani pengecualian seperti yang biasa Anda lakukan. Siklus hidup CalculatorGraph berisi tiga tahap: inisialisasi dan penyiapan, operasi grafik, dan penonaktifan grafik.

  1. Melakukan inisialisasi CalculatorGraph dengan protobuf atau biner CalculatorGraphConfig protobuf, dan menyediakan metode callback untuk mengamati output feed.

    Opsi 1. Melakukan inisialisasi CalculatorGraph dengan protobuf CalculatorGraphConfig atau representasi teksnya, dan amati streaming output-nya:

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

    Opsi 2. Melakukan inisialisasi CalculatorGraph dengan file protobuf biner, dan mengamati aliran 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. Mulai operasi grafik dan masukkan paket ke dalam grafik.

    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. Tutup grafik setelah selesai. Anda dapat memulai ulang grafik tersebut untuk grafik lain dijalankan setelah panggilan ke close().

    graph.close()
    

Skrip Python dapat dijalankan oleh runtime Python lokal Anda.