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<float> | create_float_vector([0,1, 0,2]) | get_float_list(paket, ukuran=10) |
List[str] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
Daftar[mp.Packet] | std::vector<mp::Packet> | create_packet_vector( [paket1, paket2]) |
get_packet_list(p) |
Pemetaan[str, Paket] | 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 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:
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(...); }
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
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" ], )
Bangun target ekstensi pybind (dengan akhiran .so) oleh Bazel dan pindahkan library dinamis yang dihasilkan ke salah satu direktori $LD_LIBRARY_PATH.
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.
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}'))
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))
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.