Grafik
Protokol CalculatorGraphConfig
menentukan topologi dan fungsi
Grafik MediaPipe. Setiap node
dalam grafik mewakili kalkulator tertentu atau
subgrafik, dan menentukan konfigurasi yang diperlukan, seperti
kalkulator/subgrafik, input, output, dan kolom opsional, seperti
opsi khusus node, kebijakan input dan eksekutor, yang dibahas di
Sinkronisasi.
CalculatorGraphConfig
memiliki beberapa kolom lain untuk mengonfigurasi level grafik global
setelan, mis. konfigurasi eksekutor grafik, jumlah thread, dan ukuran antrean maksimum
aliran input. Beberapa setelan tingkat grafik berguna untuk menyesuaikan
performa grafik pada berbagai platform (mis., desktop vs. seluler). Sebagai
misalnya, di perangkat seluler, melampirkan kalkulator inferensi model yang berat ke
dapat meningkatkan kinerja
aplikasi {i>real-time<i} karena ini
mengaktifkan lokalitas thread.
Di bawah ini adalah contoh CalculatorGraphConfig
sederhana yang memiliki serangkaian
kalkulator {i>passthrough<i}:
# 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 kompleks (misalnya pipeline ML, menangani 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 Membuat Grafik di C++.
Subgrafik
Untuk memodularisasi CalculatorGraphConfig
ke dalam sub-modul dan membantu penggunaan kembali
dari solusi persepsi, grafik MediaPipe dapat ditentukan sebagai Subgraph
. Tujuan
antarmuka publik dari subgrafik terdiri dari satu set {i>input<i} dan {i>output stream<i}
mirip dengan antarmuka
publik kalkulator. Subgrafik tersebut kemudian
dapat disertakan dalam
CalculatorGraphConfig
seolah-olah seperti kalkulator. Ketika grafik MediaPipe
dimuat dari CalculatorGraphConfig
, setiap node subgrafik diganti dengan
grafik kalkulator yang sesuai. Hasilnya, semantik dan performa
dari subgrafik tersebut 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 stream input
- Membuat grafik stream output
- Membuat grafik paket sisi input
- Membuat grafik paket sisi output
Daftarkan subgrafik menggunakan aturan BUILD
mediapipe_simple_subgraph
. Tujuan 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 dalam 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 menetapkan "opsi grafik" protobuf untuk grafik MediaPipe
mirip dengan Calculator Options
protobuf yang ditentukan untuk kalkulator MediaPipe. "Opsi grafik" ini dapat
menentukan di mana grafik dipanggil, dan digunakan untuk mengisi opsi kalkulator dan
dan opsi subgrafik dalam grafik tersebut.
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 kolom
nilai pada opsi kalkulator ImageToTensorCalculatorOptions
dan beberapa kolom
dalam opsi subgrafik InferenceCalculatorOptions
. Nilai kolom
ditentukan menggunakan sintaksis option_value:
.
Di protobuf CalculatorGraphConfig::Node
, kolom node_options:
dan
option_value:
bersama-sama menentukan nilai opsi untuk kalkulator seperti
ImageToTensorCalculator
. Kolom node_options:
menentukan kumpulan literal
nilai konstan menggunakan
sintaks protobuf teks. Setiap kolom option_value:
menentukan nilai untuk satu kolom protobuf menggunakan informasi dari
grafik, khususnya dari nilai bidang dalam opsi grafik
grafik. 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:
. Tujuan
sintaksisnya adalah option_value: "LHS:RHS"
. LHS mengidentifikasi opsi kalkulator
dan RHS mengidentifikasi
ruang isian opsi grafik. Lebih tepatnya, LHS
dan RHS masing-masing terdiri dari serangkaian
nama kolom protobuf yang mengidentifikasi
pesan dan kolom protobuf yang dipisahkan oleh '/'. Hal ini dikenal sebagai "ProtoPath"
sintaksis. Pesan bersarang yang dirujuk dalam LHS atau RHS harus sudah
yang ditentukan dalam protobuf yang melingkupinya untuk dapat dilalui menggunakan
option_value:
.
Musim
Secara default, MediaPipe memerlukan grafik kalkulator yang asiklik dan memperlakukan siklus dalam grafik sebagai error. Jika grafik dirancang untuk memiliki siklus, siklus itu perlu dianotasikan dalam konfigurasi grafik. Halaman ini menjelaskan cara melakukannya.
CATATAN: Pendekatan saat ini bersifat eksperimental dan dapat berubah sewaktu-waktu. Kami menyambut masukan Anda.
Gunakan pengujian unit CalculatorGraphTest.Cycle
di
mediapipe/framework/calculator_graph_test.cc
sebagai kode contoh. Yang ditampilkan di bawah ini adalah
grafik siklus dalam pengujian. Output sum
dari adder adalah jumlah dari
bilangan bulat yang dihasilkan oleh kalkulator sumber bilangan bulat.
Grafik sederhana ini menggambarkan semua masalah dalam mendukung grafik siklik.
Anotasi Back Edge
Kita mengharuskan edge dalam setiap siklus dianotasi sebagai backend. Hal ini memungkinkan Urutan topologi MediaPipe agar berfungsi, setelah semua tepi belakang dilepas.
Biasanya ada beberapa cara untuk memilih tepi belakang. Tepi mana yang ditandai karena tepi belakang mempengaruhi {i>node<i} mana yang dianggap sebagai hulu dan {i>node<i} mana yang dianggap sebagai downstream, yang pada akhirnya memengaruhi prioritas yang ditetapkan MediaPipe ke beberapa node.
Misalnya, pengujian CalculatorGraphTest.Cycle
menandai edge old_sum
sebagai
tepi belakang, sehingga node Penundaan
dianggap sebagai node downstream dari penambah
node dan diberi prioritas yang lebih tinggi. Atau, kita dapat menandai sum
input ke {i>node<i} penundaan sebagai {i>back edge<i},
dalam hal ini {i>node<i} delay akan
dianggap sebagai simpul upstream dari simpul penambah dan diberi prioritas yang lebih rendah.
Paket Awal
Agar kalkulator adder dapat dijalankan saat bilangan bulat pertama dari bilangan bulat
{i>source<i} tiba, kita membutuhkan paket
awal, dengan nilai 0 dan dengan
stempel waktu, pada stream input old_sum
ke adder. Paket awal ini
harus dihasilkan oleh kalkulator keterlambatan dalam metode Open()
.
Penundaan dalam Loop
Setiap loop harus mengalami penundaan untuk menyelaraskan output sum
sebelumnya dengan yang berikutnya
input bilangan bulat. Hal ini juga dilakukan oleh node delay. Jadi {i>node<i} delay harus
ketahui 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 berfokus pada paket memesan dan mengabaikan stempel waktu paket, yang akan menghilangkan ketidaknyamanan ini.
Penghentian Awal Kalkulator Saat Satu Streaming Input Selesai
Secara default, MediaPipe memanggil metode Close()
dari kalkulator non-sumber saat
semua aliran inputnya selesai. Pada contoh grafik, kita ingin menghentikan
{i>adder<i} segera setelah
sumber bilangan bulat selesai. Hal ini dicapai dengan
mengonfigurasi node adder dengan
pengendali aliran input alternatif,
EarlyCloseInputStreamHandler
.
Kode Sumber yang Relevan
Kalkulator Penundaan
Perhatikan kode di Open()
yang menghasilkan paket awal dan kode di
Process()
yang menambahkan penundaan (unit) untuk memasukkan paket. Seperti disebutkan di atas,
node penundaan mengasumsikan bahwa {i>output stream<i}-nya digunakan bersama aliran input
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'
}