From be0681c61df88ce2a6e4238d1cfab59833d7829f Mon Sep 17 00:00:00 2001 From: MediaPipe Team Date: Thu, 9 Feb 2023 17:18:07 -0800 Subject: [PATCH] Adds "Building Graphs in C++" initial page and updates "Graph" page to link to that section showcasing alternative C++ graph representation. PiperOrigin-RevId: 508517348 --- .../framework_concepts/building_graphs_cpp.md | 98 +++++++++++++++++++ docs/framework_concepts/graphs.md | 32 +++++- 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 docs/framework_concepts/building_graphs_cpp.md diff --git a/docs/framework_concepts/building_graphs_cpp.md b/docs/framework_concepts/building_graphs_cpp.md new file mode 100644 index 000000000..049638d96 --- /dev/null +++ b/docs/framework_concepts/building_graphs_cpp.md @@ -0,0 +1,98 @@ +--- +layout: default +title: Building Graphs in C++ +parent: Graphs +nav_order: 1 +--- + +# Building Graphs in C++ +{: .no_toc } + +1. TOC +{:toc} +--- + +## C++ Graph Builder + +C++ graph builder is a powerful tool for: + +* Building complex graphs +* Parametrizing graphs (e.g. setting a delegate on + `InferenceCalculator`, enabling/disabling parts of the graph) +* Deduplicating graphs (e.g. instead of CPU and GPU dedicated graphs in pbtxt + you can have a single code that constructs required graphs, sharing as much + as possible) +* Supporting optional graph inputs/outputs +* Customizing graphs per platform + +### Basic Usage + +Let's see how C++ graph builder can be used for a simple graph: + +```proto +// Graph inputs. +input_stream: "input_tensors" +input_side_packet: "model" + +// Graph outputs. +output_stream: "output_tensors" + +// Nodes. +node { + calculator: "InferenceCalculator" + input_stream: "TENSORS:input_tensors" + input_side_packet: "MODEL:model" + output_stream: "TENSORS:output_tensors" + node_options: { + [type.googleapis.com/mediapipe.InferenceCalculatorOptions] { + # Requesting GPU delegate. + delegate { gpu {} } + } + } +} +``` + +Function to build the above `CalculatorGraphConfig` may look like: + +```c++ +CalculatorGraphConfig BuildGraph() { + Graph graph; + + // Graph inputs. + Stream> input_tensors = + graph.In(0).SetName("input_tensors").Cast>(); + SidePacket model = + graph.SideIn(0).SetName("model").Cast(); + + // Nodes. + auto& inference_node = graph.AddNode("InferenceCalculator"); + auto& inference_opts = + inference_node.GetOptions(); + // Requesting GPU delegate. + inference_opts.mutable_delegate()->mutable_gpu(); + input_tensors.ConnectTo(inference_node.In("TENSORS")); + model.ConnectTo(inference_node.SideIn("MODEL")); + Stream> output_tensors = + inference_node.Out("TENSORS").Cast>(); + + // Graph outputs. + output_tensors.SetName("output_tensors").ConnectTo(graph.Out(0)); + + // Get `CalculatorGraphConfig` to pass it into `CalculatorGraph` + return graph.GetConfig(); +} +``` + +Short summary: + +* Use `Graph::In/SideIn` to get graph inputs as `Stream/SidePacket` +* Use `Node::Out/SideOut` to get node outputs as `Stream/SidePacket` +* Use `Stream/SidePacket::ConnectTo` to connect streams and side packets to node + inputs (`Node::In/SideIn`) and graph outputs (`Graph::Out/SideOut`) + * There's a "shortcut" operator `>>` that you can use instead of + `ConnectTo` function (E.g. `x >> node.In("IN")`). +* `Stream/SidePacket::Cast` is used to cast stream or side packet of `AnyType` (E.g. `Stream in = graph.In(0);`) to a particular type + * Using actual types instead of `AnyType` sets you on a better path for unleashing graph + builder capabilities and improving your graphs readability. + + diff --git a/docs/framework_concepts/graphs.md b/docs/framework_concepts/graphs.md index 8afec488c..5166f313b 100644 --- a/docs/framework_concepts/graphs.md +++ b/docs/framework_concepts/graphs.md @@ -36,6 +36,7 @@ passthrough calculators : # This graph named main_pass_throughcals_nosubgraph.pbtxt contains 4 # passthrough calculators. input_stream: "in" +output_stream: "out" node { calculator: "PassThroughCalculator" input_stream: "in" @@ -54,10 +55,39 @@ node { node { calculator: "PassThroughCalculator" input_stream: "out3" - output_stream: "out4" + output_stream: "out" } ``` +MediaPipe offers an alternative `C++` representation for complex graphs (e.g. ML pipelines, handling model metadata, optional nodes, etc.). The above graph may look like: + +```c++ +CalculatorGraphConfig BuildGraphConfig() { + Graph graph; + + // Graph inputs + Stream in = graph.In(0).SetName("in"); + + auto pass_through_fn = [](Stream in, + Graph& graph) -> Stream { + auto& node = graph.AddNode("PassThroughCalculator"); + in.ConnectTo(node.In(0)); + return node.Out(0); + }; + + Stream out1 = pass_through_fn(in, graph); + Stream out2 = pass_through_fn(out1, graph); + Stream out3 = pass_through_fn(out2, graph); + Stream out4 = pass_through_fn(out3, graph); + + // Graph outputs + out4.SetName("out").ConnectTo(graph.Out(0)); + + return graph.GetConfig(); +} +``` +See more details in [Building Graphs in C++](building_graphs_cpp.md) + ## Subgraph To modularize a `CalculatorGraphConfig` into sub-modules and assist with re-use