Grafik

Grafik

Proto CalculatorGraphConfig menentukan topologi dan fungsi grafik MediaPipe. Setiap node dalam grafik mewakili kalkulator atau subgrafik tertentu, dan menentukan konfigurasi yang diperlukan, seperti jenis kalkulator/subgrafik terdaftar, input, output, dan kolom opsional, seperti opsi khusus node, kebijakan input, dan eksekutor, yang dibahas dalam Sinkronisasi.

CalculatorGraphConfig memiliki beberapa kolom lain untuk mengonfigurasi setelan tingkat grafik global, misalnya konfigurasi eksekutor grafik, jumlah thread, dan ukuran antrean maksimum stream input. Beberapa setelan tingkat grafik berguna untuk menyesuaikan performa grafik pada berbagai platform (misalnya, desktop vs. seluler). Misalnya, di perangkat seluler, melampirkan kalkulator inferensi model berat ke eksekutor terpisah dapat meningkatkan performa aplikasi real-time karena memungkinkan lokalitas thread.

Di bawah ini adalah contoh CalculatorGraphConfig sederhana yang memiliki serangkaian kalkulator passthrough :

# 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 yang kompleks (misalnya pipeline ML, penanganan 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 Membangun Grafik di C++.

Subgrafik

Untuk memodularisasi CalculatorGraphConfig menjadi sub-modul dan membantu penggunaan kembali solusi persepsi, grafik MediaPipe dapat ditetapkan sebagai Subgraph. Antarmuka publik subgrafik terdiri dari serangkaian aliran input dan output yang mirip dengan antarmuka publik kalkulator. Subgrafik kemudian dapat disertakan dalam CalculatorGraphConfig seolah-olah itu adalah kalkulator. Saat grafik MediaPipe dimuat dari CalculatorGraphConfig, setiap node subgrafik diganti dengan grafik kalkulator yang sesuai. Hasilnya, semantik dan performa subgrafik identik dengan grafik kalkulator yang sesuai.

Di bawah ini adalah contoh cara membuat subgrafik bernama TwoPassThroughSubgraph.

  1. 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 streaming input
    • Membuat grafik stream output
    • Membuat grafik paket samping input
    • Membuat grafik paket samping output
  2. Daftarkan subgrafik menggunakan aturan BUILD mediapipe_simple_subgraph. Parameter register_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",
        ],
    )
    
  3. Gunakan subgrafik di 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 menentukan protobuf "opsi grafik" untuk grafik MediaPipe yang mirip dengan protobuf Calculator Options yang ditentukan untuk kalkulator MediaPipe. "Opsi grafik" ini dapat ditentukan di mana grafik dipanggil, dan digunakan untuk mengisi opsi kalkulator dan opsi subgrafik dalam grafik.

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 nilai kolom dalam opsi kalkulator ImageToTensorCalculatorOptions dan beberapa nilai kolom dalam opsi subgrafik InferenceCalculatorOptions. Nilai kolom ditentukan menggunakan sintaksis option_value:.

Dalam protobuf CalculatorGraphConfig::Node, kolom node_options: dan option_value: bersama-sama menentukan nilai opsi untuk kalkulator seperti ImageToTensorCalculator. Kolom node_options: menentukan kumpulan nilai konstanta literal menggunakan sintaksis protobuf teks. Setiap kolom option_value: menentukan nilai untuk satu kolom protobuf menggunakan informasi dari grafik penambahan, khususnya dari nilai kolom opsi grafik pada grafik penambahan. 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:. Sintaksisnya adalah option_value: "LHS:RHS". LHS mengidentifikasi isian opsi kalkulator dan RHS mengidentifikasi isian opsi grafik. Lebih khusus lagi, masing-masing LHS dan RHS terdiri dari serangkaian nama kolom protobuf yang mengidentifikasi pesan protobuf bertingkat dan kolom yang dipisahkan oleh '/'. Hal ini dikenal sebagai sintaksis "ProtoPath". Pesan bertingkat yang direferensikan dalam LHS atau RHS harus sudah ditentukan dalam protobuf yang mencakup agar dapat dilintasi menggunakan option_value:.

Musim

Secara default, MediaPipe mengharuskan grafik kalkulator menjadi asiklik dan memperlakukan siklus dalam grafik sebagai error. Jika grafik dimaksudkan untuk memiliki siklus, siklus tersebut harus dianotasi dalam konfigurasi grafik. Halaman ini menjelaskan cara melakukannya.

CATATAN: Pendekatan saat ini bersifat eksperimental dan dapat berubah sewaktu-waktu. Kami menantikan masukan Anda.

Gunakan pengujian unit CalculatorGraphTest.Cycle di mediapipe/framework/calculator_graph_test.cc sebagai kode contoh. Di bawah ini adalah grafik siklis dalam pengujian. Output sum dari penjumlahan adalah jumlah bilangan bulat yang dihasilkan oleh kalkulator sumber bilangan bulat.

grafik siklus yang menambahkan aliran bilangan bulat

Grafik sederhana ini mengilustrasikan semua masalah dalam mendukung grafik siklik.

Anotasi Tepi Belakang

Kami mengharuskan bahwa tepi dalam setiap siklus dianotasi sebagai tepi belakang. Hal ini memungkinkan pengurutan topologi MediaPipe berfungsi, setelah semua tepi belakang dihapus.

Biasanya ada beberapa cara untuk memilih tepi belakang. Tepi mana yang ditandai sebagai tepi belakang memengaruhi node mana yang dianggap sebagai upstream dan node mana yang dianggap sebagai downstream, yang nantinya memengaruhi prioritas yang ditetapkan MediaPipe ke node.

Misalnya, pengujian CalculatorGraphTest.Cycle menandai tepi old_sum sebagai tepi belakang, sehingga node Penundaan dianggap sebagai node downstream dari node penambah dan diberi prioritas yang lebih tinggi. Atau, kita dapat menandai input sum ke node penundaan sebagai tepi belakang, dalam hal ini node penundaan akan dianggap sebagai node upstream dari node penambah dan akan diberi prioritas yang lebih rendah.

Paket Awal

Agar kalkulator penjumlah dapat dijalankan saat bilangan bulat pertama dari sumber bilangan bulat tiba, kita memerlukan paket awal, dengan nilai 0 dan dengan stempel waktu yang sama, pada aliran input old_sum ke penjumlah. Paket awal ini harus di-output oleh kalkulator penundaan dalam metode Open().

Penundaan dalam Loop

Setiap loop harus mengalami penundaan untuk menyelaraskan output sum sebelumnya dengan input bilangan bulat berikutnya. Hal ini juga dilakukan oleh node delay. Jadi, node tunda perlu mengetahui hal-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 peduli pada pengurutan paket dan mengabaikan stempel waktu paket, sehingga akan menghilangkan ketidaknyamanan ini.

Penghentian Awal Kalkulator Saat Satu Streaming Input Selesai

Secara default, MediaPipe memanggil metode Close() kalkulator non-sumber saat semua aliran inputnya sudah selesai. Pada grafik contoh, kita ingin menghentikan node adder segera setelah sumber bilangan bulat selesai. Hal ini dilakukan dengan mengonfigurasi node adder dengan pengendali aliran input alternatif, EarlyCloseInputStreamHandler.

Kode Sumber yang Relevan

Kalkulator Penundaan

Catat kode dalam Open() yang menghasilkan paket awal dan kode dalam Process() yang menambahkan penundaan (unit) ke paket input. Seperti disebutkan di atas, node penundaan ini mengasumsikan bahwa aliran outputnya digunakan bersama aliran input dengan 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'
}