Grafik
Proto CalculatorGraphConfig
menentukan topologi dan fungsi
grafik MediaPipe. Setiap node
dalam grafik mewakili kalkulator atau
subgrafik tertentu, dan menentukan konfigurasi yang diperlukan, seperti
jenis kalkulator/subgrafik terdaftar, input, output, dan kolom opsional, seperti
opsi khusus node, kebijakan input, dan eksekutor, yang dibahas dalam
Sinkronisasi.
CalculatorGraphConfig
memiliki beberapa kolom lain untuk mengonfigurasi setelan tingkat grafik
global, misalnya konfigurasi eksekutor grafik, jumlah thread, dan ukuran antrean
maksimum stream input. Beberapa setelan tingkat grafik berguna untuk menyesuaikan performa grafik pada berbagai platform (misalnya, desktop vs. seluler). Misalnya, di perangkat seluler, melampirkan kalkulator inferensi model berat ke eksekutor
terpisah dapat meningkatkan performa aplikasi real-time karena
memungkinkan lokalitas thread.
Di bawah ini adalah contoh CalculatorGraphConfig
sederhana yang memiliki serangkaian
kalkulator passthrough :
# This graph named main_pass_throughcals_nosubgraph.pbtxt contains 4
# passthrough calculators.
input_stream: "in"
output_stream: "out"
node {
calculator: "PassThroughCalculator"
input_stream: "in"
output_stream: "out1"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out1"
output_stream: "out2"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out2"
output_stream: "out3"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out3"
output_stream: "out"
}
MediaPipe menawarkan representasi C++
alternatif untuk grafik yang kompleks (misalnya pipeline ML, penanganan metadata model, node opsional, dll.). Grafik di atas mungkin terlihat seperti ini:
CalculatorGraphConfig BuildGraphConfig() {
Graph graph;
// Graph inputs
Stream<AnyType> in = graph.In(0).SetName("in");
auto pass_through_fn = [](Stream<AnyType> in,
Graph& graph) -> Stream<AnyType> {
auto& node = graph.AddNode("PassThroughCalculator");
in.ConnectTo(node.In(0));
return node.Out(0);
};
Stream<AnyType> out1 = pass_through_fn(in, graph);
Stream<AnyType> out2 = pass_through_fn(out1, graph);
Stream<AnyType> out3 = pass_through_fn(out2, graph);
Stream<AnyType> out4 = pass_through_fn(out3, graph);
// Graph outputs
out4.SetName("out").ConnectTo(graph.Out(0));
return graph.GetConfig();
}
Lihat detail selengkapnya di Membangun Grafik di C++.
Subgrafik
Untuk memodularisasi CalculatorGraphConfig
menjadi sub-modul dan membantu penggunaan kembali
solusi persepsi, grafik MediaPipe dapat ditetapkan sebagai Subgraph
. Antarmuka publik subgrafik terdiri dari serangkaian aliran input dan output yang mirip dengan antarmuka publik kalkulator. Subgrafik kemudian dapat disertakan dalam
CalculatorGraphConfig
seolah-olah itu adalah kalkulator. Saat grafik MediaPipe dimuat dari CalculatorGraphConfig
, setiap node subgrafik diganti dengan grafik kalkulator yang sesuai. Hasilnya, semantik dan performa
subgrafik identik dengan grafik kalkulator yang sesuai.
Di bawah ini adalah contoh cara membuat subgrafik bernama TwoPassThroughSubgraph
.
Menentukan subgrafik.
# This subgraph is defined in two_pass_through_subgraph.pbtxt # and is registered as "TwoPassThroughSubgraph" type: "TwoPassThroughSubgraph" input_stream: "out1" output_stream: "out3" node { calculator: "PassThroughCalculator" input_stream: "out1" output_stream: "out2" } node { calculator: "PassThroughCalculator" input_stream: "out2" output_stream: "out3" }
Antarmuka publik ke subgrafik terdiri dari:
- Membuat grafik streaming input
- Membuat grafik stream output
- Membuat grafik paket samping input
- Membuat grafik paket samping output
Daftarkan subgrafik menggunakan aturan BUILD
mediapipe_simple_subgraph
. Parameterregister_as
menentukan nama komponen untuk subgrafik baru.# Small section of BUILD file for registering the "TwoPassThroughSubgraph" # subgraph for use by main graph main_pass_throughcals.pbtxt mediapipe_simple_subgraph( name = "twopassthrough_subgraph", graph = "twopassthrough_subgraph.pbtxt", register_as = "TwoPassThroughSubgraph", deps = [ "//mediapipe/calculators/core:pass_through_calculator", "//mediapipe/framework:calculator_graph", ], )
Gunakan subgrafik di grafik utama.
# This main graph is defined in main_pass_throughcals.pbtxt # using subgraph called "TwoPassThroughSubgraph" input_stream: "in" node { calculator: "PassThroughCalculator" input_stream: "in" output_stream: "out1" } node { calculator: "TwoPassThroughSubgraph" input_stream: "out1" output_stream: "out3" } node { calculator: "PassThroughCalculator" input_stream: "out3" output_stream: "out4" }
Opsi Grafik
Anda dapat menentukan protobuf "opsi grafik" untuk grafik MediaPipe yang mirip dengan protobuf Calculator Options
yang ditentukan untuk kalkulator MediaPipe. "Opsi grafik" ini dapat ditentukan di mana grafik dipanggil, dan digunakan untuk mengisi opsi kalkulator dan opsi subgrafik dalam grafik.
Dalam CalculatorGraphConfig
, opsi grafik dapat ditentukan untuk subgrafik
persis seperti opsi kalkulator, seperti yang ditunjukkan di bawah ini:
node {
calculator: "FlowLimiterCalculator"
input_stream: "image"
output_stream: "throttled_image"
node_options: {
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
max_in_flight: 1
}
}
}
node {
calculator: "FaceDetectionSubgraph"
input_stream: "IMAGE:throttled_image"
node_options: {
[type.googleapis.com/mediapipe.FaceDetectionOptions] {
tensor_width: 192
tensor_height: 192
}
}
}
Di CalculatorGraphConfig
, opsi grafik dapat diterima dan digunakan untuk mengisi
opsi kalkulator, seperti yang ditunjukkan di bawah ini:
graph_options: {
[type.googleapis.com/mediapipe.FaceDetectionOptions] {}
}
node: {
calculator: "ImageToTensorCalculator"
input_stream: "IMAGE:image"
node_options: {
[type.googleapis.com/mediapipe.ImageToTensorCalculatorOptions] {
keep_aspect_ratio: true
border_mode: BORDER_ZERO
}
}
option_value: "output_tensor_width:options/tensor_width"
option_value: "output_tensor_height:options/tensor_height"
}
node {
calculator: "InferenceCalculator"
node_options: {
[type.googleapis.com/mediapipe.InferenceCalculatorOptions] {}
}
option_value: "delegate:options/delegate"
option_value: "model_path:options/model_path"
}
Dalam contoh ini, FaceDetectionSubgraph
menerima protobuf opsi grafik
FaceDetectionOptions
. FaceDetectionOptions
digunakan untuk menentukan beberapa nilai kolom dalam opsi kalkulator ImageToTensorCalculatorOptions
dan beberapa nilai kolom dalam opsi subgrafik InferenceCalculatorOptions
. Nilai kolom
ditentukan menggunakan sintaksis option_value:
.
Dalam protobuf CalculatorGraphConfig::Node
, kolom node_options:
dan
option_value:
bersama-sama menentukan nilai opsi untuk kalkulator seperti
ImageToTensorCalculator
. Kolom node_options:
menentukan kumpulan nilai
konstanta literal menggunakan sintaksis protobuf teks. Setiap kolom option_value:
menentukan nilai untuk satu kolom protobuf menggunakan informasi dari grafik
penambahan, khususnya dari nilai kolom opsi grafik pada grafik
penambahan. Pada contoh di atas, option_value:
"output_tensor_width:options/tensor_width"
menentukan kolom
ImageToTensorCalculatorOptions.output_tensor_width
menggunakan nilai
FaceDetectionOptions.tensor_width
.
Sintaksis option_value:
mirip dengan sintaksis input_stream:
. Sintaksisnya adalah option_value: "LHS:RHS"
. LHS mengidentifikasi isian opsi kalkulator
dan RHS mengidentifikasi isian opsi grafik. Lebih khusus lagi, masing-masing LHS
dan RHS terdiri dari serangkaian nama kolom protobuf yang mengidentifikasi pesan
protobuf bertingkat dan kolom yang dipisahkan oleh '/'. Hal ini dikenal sebagai sintaksis "ProtoPath". Pesan bertingkat yang direferensikan dalam LHS atau RHS harus sudah
ditentukan dalam protobuf yang mencakup agar dapat dilintasi menggunakan
option_value:
.
Musim
Secara default, MediaPipe mengharuskan grafik kalkulator menjadi asiklik dan memperlakukan siklus dalam grafik sebagai error. Jika grafik dimaksudkan untuk memiliki siklus, siklus tersebut harus dianotasi dalam konfigurasi grafik. Halaman ini menjelaskan cara melakukannya.
CATATAN: Pendekatan saat ini bersifat eksperimental dan dapat berubah sewaktu-waktu. Kami menantikan masukan Anda.
Gunakan pengujian unit CalculatorGraphTest.Cycle
di
mediapipe/framework/calculator_graph_test.cc
sebagai kode contoh. Di bawah ini adalah grafik siklis dalam pengujian. Output sum
dari penjumlahan adalah jumlah
bilangan bulat yang dihasilkan oleh kalkulator sumber bilangan bulat.
Grafik sederhana ini mengilustrasikan semua masalah dalam mendukung grafik siklik.
Anotasi Tepi Belakang
Kami mengharuskan bahwa tepi dalam setiap siklus dianotasi sebagai tepi belakang. Hal ini memungkinkan pengurutan topologi MediaPipe berfungsi, setelah semua tepi belakang dihapus.
Biasanya ada beberapa cara untuk memilih tepi belakang. Tepi mana yang ditandai sebagai tepi belakang memengaruhi node mana yang dianggap sebagai upstream dan node mana yang dianggap sebagai downstream, yang nantinya memengaruhi prioritas yang ditetapkan MediaPipe ke node.
Misalnya, pengujian CalculatorGraphTest.Cycle
menandai tepi old_sum
sebagai
tepi belakang, sehingga node Penundaan dianggap sebagai node downstream dari node
penambah dan diberi prioritas yang lebih tinggi. Atau, kita dapat menandai input sum
ke node penundaan sebagai tepi belakang, dalam hal ini node penundaan akan
dianggap sebagai node upstream dari node penambah dan akan diberi prioritas yang lebih rendah.
Paket Awal
Agar kalkulator penjumlah dapat dijalankan saat bilangan bulat pertama dari sumber bilangan bulat tiba, kita memerlukan paket awal, dengan nilai 0 dan dengan stempel waktu yang sama, pada aliran input old_sum
ke penjumlah. Paket awal ini
harus di-output oleh kalkulator penundaan dalam metode Open()
.
Penundaan dalam Loop
Setiap loop harus mengalami penundaan untuk menyelaraskan output sum
sebelumnya dengan input bilangan bulat berikutnya. Hal ini juga dilakukan oleh node delay. Jadi, node tunda perlu
mengetahui hal-hal berikut tentang stempel waktu kalkulator sumber bilangan bulat:
Stempel waktu output pertama.
Delta stempel waktu di antara output yang berurutan.
Kami berencana untuk menambahkan kebijakan penjadwalan alternatif yang hanya peduli pada pengurutan paket dan mengabaikan stempel waktu paket, sehingga akan menghilangkan ketidaknyamanan ini.
Penghentian Awal Kalkulator Saat Satu Streaming Input Selesai
Secara default, MediaPipe memanggil metode Close()
kalkulator non-sumber saat
semua aliran inputnya sudah selesai. Pada grafik contoh, kita ingin menghentikan
node adder segera setelah sumber bilangan bulat selesai. Hal ini dilakukan dengan
mengonfigurasi node adder dengan pengendali aliran input alternatif,
EarlyCloseInputStreamHandler
.
Kode Sumber yang Relevan
Kalkulator Penundaan
Catat kode dalam Open()
yang menghasilkan paket awal dan kode dalam
Process()
yang menambahkan penundaan (unit) ke paket input. Seperti disebutkan di atas, node penundaan ini mengasumsikan bahwa aliran outputnya digunakan bersama aliran input dengan
stempel waktu paket 0, 1, 2, 3, ...
class UnitDelayCalculator : public Calculator {
public:
static absl::Status FillExpectations(
const CalculatorOptions& extendable_options, PacketTypeSet* inputs,
PacketTypeSet* outputs, PacketTypeSet* input_side_packets) {
inputs->Index(0)->Set<int>("An integer.");
outputs->Index(0)->Set<int>("The input delayed by one time unit.");
return absl::OkStatus();
}
absl::Status Open() final {
Output()->Add(new int(0), Timestamp(0));
return absl::OkStatus();
}
absl::Status Process() final {
const Packet& packet = Input()->Value();
Output()->AddPacket(packet.At(packet.Timestamp().NextAllowedInStream()));
return absl::OkStatus();
}
};
Konfigurasi Grafik
Perhatikan anotasi back_edge
dan input_stream_handler
alternatif.
node {
calculator: 'GlobalCountSourceCalculator'
input_side_packet: 'global_counter'
output_stream: 'integers'
}
node {
calculator: 'IntAdderCalculator'
input_stream: 'integers'
input_stream: 'old_sum'
input_stream_info: {
tag_index: ':1' # 'old_sum'
back_edge: true
}
output_stream: 'sum'
input_stream_handler {
input_stream_handler: 'EarlyCloseInputStreamHandler'
}
}
node {
calculator: 'UnitDelayCalculator'
input_stream: 'sum'
output_stream: 'old_sum'
}