圖表

圖表

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,就像計算機一樣。當 MediaPipe 圖表顯示 從 CalculatorGraphConfig 載入,每個子圖表節點都會替換為 以及相應的計算機圖形因此,語意和效能 就會對應至相應的計算機圖形。

以下範例說明如何建立名為 TwoPassThroughSubgraph 的子圖表。

  1. 定義子圖表。

    # 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"
    }
    

    子圖表的公用介麵包含:

    • 繪製輸入串流的圖形
    • 繪製輸出串流的圖形
    • 繪製輸入端封包的圖形
    • 繪製側邊封包的圖表
  2. 使用 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",
        ],
    )
    
  3. 使用主圖中的子圖表。

    # 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 與 Calculator Options 類似 為 MediaPipe 計算機指定的 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 FaceDetectionOptionsFaceDetectionOptions 是用來定義部分欄位 計算機選項 ImageToTensorCalculatorOptions 和部分欄位中的值 子圖表選項 InferenceCalculatorOptions 中的值。欄位值 是使用 option_value: 語法來定義

CalculatorGraphConfig::Node protobuf 中的欄位 node_options:option_value: 一起定義計算機的選項值,例如 ImageToTensorCalculatornode_options: 欄位會定義一組常值 常數值。每個 option_value: 欄位 會定義一個通訊協定緩衝區欄位的值 圖表,特別是來自資料集內圖表選項的欄位值 圖表。在上述範例中,option_value: "output_tensor_width:options/tensor_width" 定義欄位 使用 ImageToTensorCalculatorOptions.output_tensor_width 的值做為 FaceDetectionOptions.tensor_width

option_value: 的語法與 input_stream: 的語法類似。 語法為 option_value: "LHS:RHS"LHS 會找出計算機選項 欄位,RHS 則識別圖表選項欄位。具體來說 而 RHS 則包含一系列的 protobuf 欄位名稱 protobuf 訊息和欄位,以「/」分隔。這就是所謂的「ProtoPath」 語法。LHS 或 RHS 參照的巢狀訊息必須已 才能完全依照 option_value:

時序更迭

依據預設,MediaPipe 要求計算機圖形必須為循環圖形和治療週期 顯示為錯誤如果圖表涉及月經週期,那麼循環機制必須 加註。本頁說明如何完成這項作業。

注意:目前做法仍在實驗階段,因此可能會有變動。不客氣 提供意見回饋。

請在以下位置使用 CalculatorGraphTest.Cycle 單元測試: 以 mediapipe/framework/calculator_graph_test.cc 做為範例程式碼。如下所示 測試中的循環圖加法器的 sum 輸出內容是 整數來源計算工具產生的整數。

加入整數串流的循環圖

這個簡單的圖表呈現了支援循環圖的所有問題。

返回邊緣註解

每個週期的邊緣都必須加註為後邊緣。這樣一來, 移除所有返回邊緣後,MediaPipe 的拓撲排序。

一般來說,選取後邊緣的方法有很多種。哪些邊緣所標示的邊緣 因為返回邊緣會影響哪些節點被視為上游,以及哪些節點 這會影響 MediaPipe 指派的優先順序 節點。

舉例來說,CalculatorGraphTest.Cycle 測試會將 old_sum 邊緣標示為 因此 Delay 節點會被視為其下游節點 且優先順序較高或者,您也可以將 sum 若是後邊緣,延遲時間節點就會是 視為附加器節點的上游節點,優先順序較低

初始封包

為了讓新增器計算器在整數中的第一個整數為執行時 收到來源時,我們需要使用值為 0 且具有相同值的初始封包 old_sum 輸入串流上的時間戳記,這個初始封包 應使用 Open() 方法的延遲計算工具輸出。

迴圈延遲

每個迴圈都應該產生延遲,以便對齊先前的 sum 輸出內容 整數值。這也適用於延遲節點。所以延遲節點 瞭解有關整數來源計算機的時間戳記:

  • 第一項輸出內容的時間戳記。

  • 連續輸出內容之間的時間戳記差異。

我們打算新增僅適用於封包的其他排程政策 排序並忽略封包時間戳記,即可排除造成的不便。

在單一輸入串流完成時提前終止計算機

根據預設,在下列情況下,MediaPipe 會呼叫非來源計算機的 Close() 方法 所有輸入串流都已完成在範例中,我們想停止 加上器節點。方法是透過 使用替代輸入串流處理常式設定 adder 節點 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'
}