圖表
CalculatorGraphConfig
proto 可指定 MediaPipe 圖表的拓撲和功能。圖表中的每個 node
都代表一個特定計算機或子圖表,並指定必要的設定,例如已註冊的計算機/子圖表類型、輸入、輸出和選填欄位,例如節點專屬選項、輸入政策和執行程式,詳情請參閱同步處理。
CalculatorGraphConfig
還有數個其他欄位,用於進行全域圖表層級設定,例如圖表執行工具設定、執行緒數量,以及輸入串流的佇列大小上限。如要調整不同平台 (例如電腦和行動裝置) 的圖表成效,有部分圖形層級設定非常實用。舉例來說,如果在行動裝置上,將繁重的模型推論計算機附加至獨立執行程式,可以提升即時應用程式的效能,因為這樣可以啟用執行緒位置。
以下是一個簡單的 CalculatorGraphConfig
範例,其中有一系列的直通計算機:
# 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 提供複雜圖表的替代 C++
表示法 (例如機器學習管道、處理模型中繼資料、選用節點等)。上圖可能如下:
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();
}
詳情請參閱在 C++ 中建構圖表。
子圖表
如要將 CalculatorGraphConfig
模組化為子模組,並協助重複使用感知解決方案,可將 MediaPipe 圖表定義為 Subgraph
。子圖表的公開介面由一組輸入和輸出串流組成,類似於計算機的公開介面。接著,就可以把子圖形納入 CalculatorGraphConfig
中,就像是計算機一樣。從 CalculatorGraphConfig
載入 MediaPipe 圖表時,每個子圖表節點都會替換為對應的計算機圖形。因此,子圖表的語意和效能與對應的計算器圖形完全相同。
以下範例說明如何建立名為 TwoPassThroughSubgraph
的子圖表。
定義子圖表。
# 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" }
子圖表的公開介麵包括:
- 圖形輸入串流
- 圖表輸出串流
- 繪製輸入端封包的圖形
- 繪製輸出側封包的圖形
使用 BUILD 規則
mediapipe_simple_subgraph
註冊子圖形。參數register_as
會定義新子圖表的元件名稱。# 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", ], )
使用主圖表的子圖表。
# 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" }
圖表選項
您可以為 MediaPipe 圖表指定「圖表選項」protobuf,類似於為 MediaPipe 計算機指定的 Calculator Options
protobuf。這些「圖表選項」可以指定叫用圖表的位置,並用來在圖形中填入計算機選項和子圖表選項。
在 CalculatorGraphConfig
中,您可以為子圖表指定與計算機選項完全相同的圖表選項,如下所示:
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
}
}
}
在 CalculatorGraphConfig
中,接受圖表選項並用於填入計算機選項,如下所示:
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"
}
在這個範例中,FaceDetectionSubgraph
接受圖表選項 protobuf FaceDetectionOptions
。FaceDetectionOptions
是用來定義計算機選項 ImageToTensorCalculatorOptions
中的部分欄位值,以及子圖表選項 InferenceCalculatorOptions
中的部分欄位值。欄位值是以 option_value:
語法定義。
在 CalculatorGraphConfig::Node
protobuf 中,node_options:
和 option_value:
欄位會一起定義計算機的選項值,例如 ImageToTensorCalculator
。node_options:
欄位會使用文字 protobuf 語法定義一組常值常數值。每個 option_value:
欄位都會使用封閉圖表的資訊 (尤其是外框圖形選項的欄位值) 定義一個 protobuf 欄位的值。在上述範例中,option_value:
"output_tensor_width:options/tensor_width"
使用 FaceDetectionOptions.tensor_width
的值定義 ImageToTensorCalculatorOptions.output_tensor_width
欄位。
option_value:
的語法與 input_stream:
的語法類似。語法為 option_value: "LHS:RHS"
。LHS 識別計算機選項欄位,RHS 則識別圖形選項欄位。具體來說,LHS 和 RHS 分別包含一系列 protobuf 欄位名稱,以識別巢狀 protobuf 訊息,以及以「/」分隔的欄位。這就是所謂的「ProtoPath」語法。LHS 或 RHS 中參照的巢狀訊息必須在封閉的 protobuf 中定義,才能使用 option_value:
進行掃遍。
時序更迭
根據預設,MediaPipe 需要計算機圖形具有循環特性,並且將圖表中的週期視為錯誤。如果圖表包含週期,您必須在圖形設定中為週期加上註解。本頁說明如何進行這項操作。
注意:目前做法為實驗性質,可能會有變動。歡迎提供意見。
請使用 mediapipe/framework/calculator_graph_test.cc
中的 CalculatorGraphTest.Cycle
單元測試做為程式碼範例。下圖是測試中的循環圖。加法器的 sum
輸出是整數來源計算工具產生的整數總和。
這張簡易圖表展示了支援循環圖的所有問題。
後端邊緣註解
我們要求每個循環的邊緣被註釋為後端。如此一來,在移除所有後方邊緣後,MediaPipe 的拓撲排序就能正常運作。
您通常可以透過多種方式選取後緣。哪些邊緣標示為後端邊緣會影響哪些節點會被視為上游,以及哪些節點會被認定為下游,而這也會影響 MediaPipe 指派給節點的優先順序。
舉例來說,CalculatorGraphTest.Cycle
測試會將 old_sum
邊緣標示為返回邊緣,因此系統會將延遲節點視為新增器節點的下游節點,並給予較高的優先順序。或者,我們可以將延遲節點的 sum
輸入內容標示為後端邊緣,在此情況下,系統會將延遲節點視為新增器節點的上游節點,並給予較低的優先順序。
初始封包
為了讓新增器計算機在整數來源的第一個整數送達時執行,我們需要在 old_sum
輸入串流中取得值為 0 且時間戳記相同的初始封包。這個初始封包應由 Open()
方法中的延遲計算機輸出。
迴圈延遲
每個迴圈都必須發生延遲,才能將先前的 sum
輸出內容與下一個整數輸入內容對齊。這也是由延遲節點執行。因此,延遲節點需要瞭解以下整數來源計算工具的時間戳記:
第一個輸出內容的時間戳記。
連續輸出之間的時間戳記差異。
我們計劃增加額外的排程政策,其重點放在封包排序,並忽略封包時間戳記,以解決這類不便。
單一輸入串流完成時終止計算機
根據預設,MediaPipe 會在所有輸入串流完成時,呼叫非來源計算機的 Close()
方法。在範例圖表中,我們希望在整數來源完成時立即停止新增節點。方法是使用替代輸入串流處理常式 EarlyCloseInputStreamHandler
來設定新增器節點。
相關原始碼
延遲計算機
請注意,Open()
中會輸出初始封包的程式碼,以及 Process()
中的程式碼,這會為輸入封包加上 (單位) 延遲時間。如上所述,這個延遲節點會假設其輸出串流會與具有封包時間戳記 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();
}
};
圖表設定
請留意 back_edge
註解和 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'
}