Grafikler

Grafik

CalculatorGraphConfig protosu, bir MediaPipe grafiğinin topolojisini ve işlevselliğini belirtir. Grafikteki her node, belirli bir hesap makinesini veya alt grafiği temsil eder ve Senkronizasyon bölümünde ele alınan kayıtlı hesap makinesi/alt grafik türü, girişler, çıkışlar ve düğüme özgü seçenekler, giriş politikası ve yürütücü gibi isteğe bağlı alanlar gibi gerekli yapılandırmaları belirtir.

CalculatorGraphConfig, grafik düzeyindeki genel ayarları yapılandırmak için birkaç alana daha sahiptir (ör. grafik yürütücü yapılandırmaları, iş parçacığı sayısı ve giriş akışlarının maksimum sıra boyutu). Farklı platformlardaki (ör. masaüstü ve mobil) grafiğin performansını ayarlamak için çeşitli grafik düzeyindeki ayarlar yararlıdır. Örneğin, mobil cihazlarda ayrı bir yürütücüye ağır bir model çıkarım hesaplayıcısı eklemek, iş parçacığı yerelliğini etkinleştirdiği için gerçek zamanlı bir uygulamanın performansını artırabilir.

Aşağıda, bir dizi doğrudan geçiş hesaplayıcımızın bulunduğu önemsiz bir CalculatorGraphConfig örneği verilmiştir :

# 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 karmaşık grafikler (ör. makine öğrenimi ardışık düzenleri, model meta verilerini işleme, isteğe bağlı düğümler vb.) için alternatif bir C++ temsili sunar. Yukarıdaki grafik şöyle görünebilir:

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();
}

Daha fazla ayrıntı için C++ Kullanarak Grafik Oluşturma konusuna bakın.

Alt grafik

CalculatorGraphConfig öğesini alt modüllere modüler hale getirmek ve algı çözümlerinin yeniden kullanımına yardımcı olmak için bir MediaPipe grafiği Subgraph olarak tanımlanabilir. Alt grafiğin genel arayüzü, hesap makinesinin herkese açık arayüzüne benzer bir dizi giriş ve çıkış akışından oluşur. Alt grafik daha sonra bir hesap makinesi gibi CalculatorGraphConfig içine eklenebilir. CalculatorGraphConfig üzerinden bir MediaPipe grafiği yüklendiğinde, her alt grafik düğümü, karşılık gelen hesaplayıcı grafiği ile değiştirilir. Sonuç olarak, alt grafiğin anlamı ve performansı, karşılık gelen hesaplayıcı grafiği ile aynıdır.

Aşağıda, TwoPassThroughSubgraph adlı bir alt grafiğin nasıl oluşturulacağına ilişkin bir örnek verilmiştir.

  1. Alt grafiği tanımlama.

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

    Altyazının herkese açık arayüzü şu bölümlerden oluşur:

    • Giriş akışlarının grafiğini çizme
    • Çıkış akışlarının grafiğini çizme
    • Giriş tarafı paketlerinin grafiğini çizme
    • Çıkış tarafı paketlerinin grafiğini çizme
  2. mediapipe_simple_subgraph DERLEME kuralını kullanarak alt grafiği kaydedin. register_as parametresi, yeni alt grafiğin bileşen adını tanımlar.

    # 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. Alt grafiği ana grafikte kullanın.

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

Grafik Seçenekleri

MediaPipe hesap makinesi için belirtilen Calculator Options protobuf'a benzer bir MediaPipe grafiği için "grafik seçenekleri" protobuf'u belirtmek mümkündür. Bu "grafik seçenekleri", bir grafiğin çağrıldığı yerde belirtilebilir ve grafikteki hesap makinesi seçeneklerini ve alt grafik seçeneklerini doldurmak için kullanılabilir.

CalculatorGraphConfig'de, bir alt grafik için grafik seçenekleri, aşağıda gösterildiği gibi, hesap makinesi seçeneklerine benzer bir şekilde belirtilebilir:

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 öğesinde grafik seçenekleri kabul edilebilir ve aşağıda gösterildiği gibi hesap makinesi seçeneklerini doldurmak için kullanılabilir:

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

Bu örnekte FaceDetectionSubgraph, FaceDetectionOptions grafik seçeneği protokolünü kabul eder. FaceDetectionOptions, hesap makinesi seçeneklerindeki ImageToTensorCalculatorOptions bazı alan değerlerini ve InferenceCalculatorOptions alt grafik seçeneklerinde bazı alan değerlerini tanımlamak için kullanılır. Alan değerleri, option_value: söz dizimi kullanılarak tanımlanır.

CalculatorGraphConfig::Node protokolünde, node_options: ve option_value: alanları birlikte ImageToTensorCalculator gibi bir hesap makinesinin seçenek değerlerini tanımlar. node_options: alanı, metin protobuf söz dizimini kullanarak bir sabit değer grubu tanımlar. Her option_value: alanı, özellikle de çevreleyen grafiğin grafik seçeneklerinin alan değerlerinden alınan bilgileri kullanarak bir protobuf alanının değerini tanımlar. Yukarıdaki örnekte option_value: "output_tensor_width:options/tensor_width", FaceDetectionOptions.tensor_width değerini kullanarak ImageToTensorCalculatorOptions.output_tensor_width alanını tanımlar.

option_value: söz dizimi input_stream: söz dizimine benzerdir. Söz dizimi option_value: "LHS:RHS" şeklinde. LHS, hesap makinesi seçenek alanını, RHS ise grafik seçeneği alanını tanımlar. Daha ayrıntılı belirtmek gerekirse, LHS ve RHS'nin her biri, iç içe yerleştirilmiş protobuf mesajlarını ve "/" ile ayrılmış alanları tanımlayan bir dizi protobuf alan adından oluşur. Bu, "ProtoPath" söz dizimi olarak bilinir. LHS veya RHS'de başvurulan iç içe geçmiş mesajların option_value: kullanılarak aktarılabilmesi için çevreleyen protokol arabelleğinde zaten tanımlanmış olması gerekir.

Hayat Döngüleri

MediaPipe, varsayılan olarak hesap makinesi grafiklerinin döngüsel olmasını gerektirir ve grafikteki döngüleri hata olarak ele alır. Bir grafiğin döngüler içermesi amaçlanıyorsa döngüler, grafik yapılandırmasında ek açıklama olarak belirtilmelidir. Bu sayfada, bunun nasıl yapılacağı açıklanmaktadır.

NOT: Mevcut yaklaşım deneme amaçlıdır ve değişebilir. Geri bildirimlerinizi bekliyoruz.

Lütfen mediapipe/framework/calculator_graph_test.cc dilindeki CalculatorGraphTest.Cycle birim testini örnek kod olarak kullanın. Aşağıda testteki döngüsel grafik gösterilmektedir. Toplayıcının sum çıktısı, tam sayı kaynak hesaplayıcısı tarafından oluşturulan tam sayıların toplamıdır.

tam sayı akışı ekleyen döngüsel grafik

Bu basit grafik, destekleyici döngüsel grafiklerdeki tüm sorunları gösterir.

Arka Kenar Ek Açıklaması

Her döngüdeki bir kenara, arka kenar olarak ek açıklama girilmesi gerekir. Bu, tüm arka kenarlar kaldırıldıktan sonra MediaPipe'in topolojik sıralamasının çalışmasını sağlar.

Arka kenarları seçmenin genellikle birden çok yolu vardır. Hangi kenarların arka kenar olarak işaretleneceği, hangi düğümlerin yukarı akış, hangi düğümlerin aşağı akış olarak kabul edileceğini etkiler. Bu da MediaPipe'in düğümlere atadığı öncelikleri etkiler.

Örneğin, CalculatorGraphTest.Cycle testi, old_sum kenarını arka uç olarak işaretler. Bu nedenle Gecikme düğümü, toplayıcı düğümün aşağı akış düğümü olarak kabul edilir ve bu düğüme daha yüksek bir öncelik verilir. Alternatif olarak, gecikme düğümüne sum girişini arka kenar olarak işaretleyebiliriz. Bu durumda, gecikme düğümü, toplayıcı düğümün yukarı akış düğümü olarak değerlendirilir ve daha düşük bir öncelik verilir.

İlk Paket

Tam sayı kaynağından ilk tam sayı geldiğinde toplayıcı hesap makinesinin çalıştırılabilmesi için toplayıcıya giden old_sum giriş akışında aynı zaman damgasına sahip 0 değerine sahip bir başlangıç paketine ihtiyacımız vardır. Bu başlangıç paketinin çıktısı, Open() yöntemindeki gecikme hesaplayıcı ile yapılmalıdır.

Döngüde Gecikme

Her döngü, önceki sum çıkışını sonraki tam sayı girişiyle hizalamak için gecikmeye neden olmalıdır. Bu, gecikme düğümü tarafından da yapılır. Bu nedenle, gecikme düğümünün tamsayı kaynak hesaplayıcının zaman damgaları hakkında aşağıdakileri bilmesi gerekir:

  • İlk çıkışın zaman damgası.

  • Ardışık çıkışlar arasındaki zaman damgası deltası.

Yalnızca paket siparişiyle ilgilenen ve paket zaman damgalarını yok sayan alternatif bir planlama politikası ekleyerek bu rahatsızlığı ortadan kaldırmayı planlıyoruz.

Tek Bir Girişli Akış Tamamlandığında Hesap Makinesinin Erken Sonlandırılması

Tüm giriş akışları tamamlandığında MediaPipe varsayılan olarak kaynak olmayan hesap makinesinin Close() yöntemini çağırır. Örnek grafikte, tamsayı kaynağı biter bitmez ekleyici düğümü durdurmak istiyoruz. Bu işlem, toplayıcı düğümünü alternatif bir giriş akışı işleyicisi (EarlyCloseInputStreamHandler) ile yapılandırarak gerçekleştirilir.

Alakalı Kaynak Kodu

Gecikme Hesaplayıcı

Open() içindeki ve ilk paketi çıkaran kodu ve giriş paketlerine (birim) gecikme ekleyen Process() kodundaki kodu not edin. Yukarıda belirtildiği gibi bu gecikmeli düğüm, çıkış akışının 0, 1, 2, 3, ... paket zaman damgalarına sahip bir giriş akışıyla birlikte kullanıldığını varsayar.

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();
  }
};

Grafik Yapılandırması

back_edge ek açıklamasına ve alternatif input_stream_handler değerine dikkat edin.

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'
}