Grafik
Ein CalculatorGraphConfig
-Proto gibt die Topologie und Funktionalität eines
MediaPipe-Diagramm Jeder node
im Diagramm steht für einen bestimmten Rechner oder
Teilgraph auf und gibt notwendige Konfigurationen an, z. B. registrierte
Rechner-/Untergrafiktyp, Eingaben, Ausgaben und optionale Felder wie
Knotenspezifische Optionen, Eingaberichtlinie und Executor, die in
Synchronisierung.
CalculatorGraphConfig
hat mehrere weitere Felder zum Konfigurieren der globalen Grafikebene
Einstellungen, z.B. Graph Executor-Konfigurationen, Anzahl der Threads und maximale Warteschlangengröße
von Eingabestreams. Mehrere Einstellungen auf Diagrammebene sind hilfreich, um den
Leistung des Diagramms auf verschiedenen Plattformen (z. B. Computer und Mobilgeräte) Für
können Sie auf Mobilgeräten einen umfangreichen Rechner für die Modellinferenz an einen separaten
Executor kann die Leistung einer Echtzeitanwendung verbessern, da dies
aktiviert die Thread-Lokalität.
Unten sehen Sie ein einfaches CalculatorGraphConfig
-Beispiel, in dem eine Reihe von
Passthrough-Rechner :
# 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 bietet eine alternative C++
-Darstellung für komplexe Grafiken (z.B. ML-Pipelines, Verarbeitung von Modellmetadaten, optionale Knoten usw.). Das obige Diagramm könnte so aussehen:
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();
}
Weitere Informationen finden Sie unter Grafiken in C++ erstellen.
Teildiagramm
Um ein CalculatorGraphConfig
in untergeordnete Module zu modularisieren und bei der Wiederverwendung zu unterstützen
Wahrnehmungslösungen kann ein MediaPipe-Diagramm als Subgraph
definiert werden. Die
Die öffentliche Schnittstelle eines Subgraphs besteht aus einer Reihe von Eingabe- und Ausgabestreams.
ähnlich wie die öffentliche Benutzeroberfläche
eines Rechners. Der Teilgraph kann dann in
CalculatorGraphConfig
wie ein Rechner. Wenn ein MediaPipe-Diagramm
aus einem CalculatorGraphConfig
geladen, wird jeder Teilgraphknoten durch den
entsprechenden Rechner. Daraus folgt, dass die Semantik und Leistung
des Teilgraphen identisch
mit dem entsprechenden Rechnerdiagramm.
Im Folgenden finden Sie ein Beispiel für das Erstellen eines Teildiagramms mit dem Namen TwoPassThroughSubgraph
.
Definieren des Teilgraphs.
# 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" }
Die öffentliche Schnittstelle für den Subgraph besteht aus:
- Grafikeingabestreams
- Grafikausgabestreams
- Pakete auf der Eingabeseite grafisch darstellen
- Ausgabeseitenpakete grafisch darstellen
Registrieren Sie den Teilgraph mit der Build-Regel
mediapipe_simple_subgraph
. Die Der Parameterregister_as
definiert den Komponentennamen für die neue Teilgrafik.# 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", ], )
Verwenden Sie die Teilgrafik in der Hauptgrafik.
# 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" }
Grafikoptionen
Es ist möglich, Grafikoptionen protobuf für einen MediaPipe-Graphen
ähnlich wie Calculator Options
protobuf, der für einen MediaPipe-Rechner angegeben wurde. Diese "Grafikoptionen" kann sein
wird angegeben, wo eine Grafik aufgerufen wird, und wird zum Ausfüllen von Rechneroptionen und
Teilgrafikoptionen innerhalb der Grafik.
In einem CalculatorGraphConfig
können Grafikoptionen für einen Teildiagramm angegeben werden
genau wie bei den Rechneroptionen:
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
}
}
}
In einem CalculatorGraphConfig
können Grafikoptionen akzeptiert und zum Ausfüllen verwendet werden
Rechneroptionen wie unten gezeigt:
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"
}
In diesem Beispiel akzeptiert FaceDetectionSubgraph
die Graph-Option „protobuf“
FaceDetectionOptions
. Mit FaceDetectionOptions
wird ein Feld definiert
Werte in den Rechneroptionen ImageToTensorCalculatorOptions
und einigen Feldern
Werte in den Teilgrafikoptionen InferenceCalculatorOptions
Die Feldwerte
werden mit der Syntax option_value:
definiert.
Im Protokollzwischenspeicher CalculatorGraphConfig::Node
werden die Felder node_options:
und
option_value:
definieren gemeinsam die Optionswerte für einen Rechner:
ImageToTensorCalculator
. Das Feld node_options:
definiert eine Reihe von Literalen
mit der protobuf-Textsyntax konstante Werte festlegen. Jedes option_value:
-Feld
definiert den Wert für ein protobuf-Feld mithilfe von Informationen aus dem einschließenden
insbesondere aus Feldwerten der Grafikoptionen der einschließenden
Diagramm. Im obigen Beispiel hat die option_value:
"output_tensor_width:options/tensor_width"
definiert das Feld
ImageToTensorCalculatorOptions.output_tensor_width
mit dem Wert von
FaceDetectionOptions.tensor_width
.
Die Syntax von option_value:
ähnelt der Syntax von input_stream:
. Die
Die Syntax lautet option_value: "LHS:RHS"
. Auf der linken Seite ist eine Taschenrechneroption zu sehen.
und auf der rechten Seite das Feld mit den Grafikoptionen. Genauer gesagt, auf der linken Seite
und RHS besteht jeweils aus einer Reihe von protobuf-Feldnamen, die verschachtelte
protobuf-Nachrichten und -Felder, die durch '/' getrennt sind. Dies wird als „ProtoPath“
Syntax. Verschachtelte Nachrichten, auf die links oder rechts verwiesen wird, müssen bereits
im einschließenden Protokollzwischenspeicher definiert,
option_value:
Tages- & Jahreszeiten
Standardmäßig muss MediaPipe Rechnerdiagramme azyklisch sein und behandelt Zyklen in einer Grafik als Fehler an. Wenn ein Diagramm Zyklen enthalten soll, müssen die Zyklen in der Grafikkonfiguration annotiert werden. Auf dieser Seite wird beschrieben, wie Sie dazu vorgehen.
HINWEIS: Der aktuelle Ansatz ist experimentell und kann sich ändern. Gern begrüßen wir Feedback geben.
Verwenden Sie den Einheitentest CalculatorGraphTest.Cycle
in
mediapipe/framework/calculator_graph_test.cc
als Beispielcode. Unten sehen Sie
den zyklischen Graphen im Test. Die sum
-Ausgabe des Addierers ist die Summe der
Ganzzahlen, die vom Integer-Quellenrechner generiert wurden.
Dieses einfache Diagramm veranschaulicht alle Probleme bei der Unterstützung zyklischer Graphen.
Back-Edge-Anmerkung
Eine Kante in jedem Zyklus muss als Hinterkante gekennzeichnet sein. Dadurch können Sie Die topologische Sortierung von MediaPipe funktioniert nach dem Entfernen aller Hinterkanten.
Normalerweise gibt es mehrere Möglichkeiten, die hinteren Kanten auszuwählen. Welche Kanten sind markiert? da Hinterkanten sich darauf auswirken, welche Knoten als Upstream angesehen werden und welche Knoten werden als nachgelagert betrachtet, was sich wiederum auf die Prioritäten auswirkt, die MediaPipe zuweist. zu den Knoten.
Zum Beispiel markiert der CalculatorGraphTest.Cycle
-Test die Kante old_sum
als
Back Edge, damit der Delay-Knoten als nachgelagerter Knoten des Addierers betrachtet wird
Knoten und erhält eine höhere Priorität. Alternativ können wir das sum
-Objekt
Eingabe für den Verzögerungsknoten als Back Edge. In diesem Fall wäre der Verzögerungsknoten
wird als vorgelagerter Knoten des Adder-Knotens betrachtet und erhält eine niedrigere Priorität.
Anfangspaket
Damit der Additionsrechner ausgeführt werden kann, wenn die erste Ganzzahl der Ganzzahl
und Quelle angekommen ist, brauchen wir ein erstes Paket mit dem Wert 0
Zeitstempel im old_sum
-Eingabestream für die Addierer. Dieses erste Paket
sollte vom Verzögerungsrechner in der Methode Open()
ausgegeben werden.
Verzögerung in einer Schleife
In jeder Schleife sollte eine Verzögerung auftreten, um die vorherige sum
-Ausgabe an die nächste anzupassen
Ganzzahleingabe. Dies geschieht ebenfalls durch den Verzögerungsknoten. Der Verzögerungsknoten muss also
über die Zeitstempel des Integer-Quellenrechners wissen:
Der Zeitstempel der ersten Ausgabe.
Zeitstempel-Delta zwischen aufeinanderfolgenden Ausgaben.
Wir planen die Einführung einer alternativen Planungsrichtlinie, die sich nur auf das Paket bezieht und ignoriert Paketzeitstempel, wodurch diese Unannehmlichkeiten vermieden werden.
Vorzeitige Beendigung eines Rechners nach Fertigstellung eines Eingabestreams
Standardmäßig ruft MediaPipe die Close()
-Methode eines externen Rechners auf, wenn
alle Eingabestreams abgeschlossen sind. In der Beispielgrafik wollen wir die
Adder-Knoten hinzu, sobald die Ganzzahlquelle abgeschlossen ist. Dies wird erreicht, indem
Konfigurieren des Adder-Knotens mit einem alternativen Eingabestream-Handler
EarlyCloseInputStreamHandler
Relevanter Quellcode
Verzögerungsrechner
Achten Sie auf den Code in Open()
, der das ursprüngliche Paket und den Code in
Process()
, die den Eingabepaketen eine (Einheits-)Verzögerung hinzufügt. Wie bereits erwähnt,
Verzögerungsknoten geht davon aus, dass sein Ausgabestream zusammen mit einem Eingabestream mit
Paketzeitstempel 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();
}
};
Grafikkonfiguration
Beachten Sie die Annotation back_edge
und die alternative Annotation input_stream_handler
.
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'
}