Her hesap makinesi, bir grafik düğümüdür. Yeni bir
hesap makinesi, hesap makinesi nasıl başlatılır, hesaplama nasıl yapılır,
giriş ve çıkış akışları, zaman damgaları ve seçenekler. Grafikteki her düğüm
Calculator
olarak uygulanır. Grafik yürütmenin büyük kısmı
hesap makineleri. Bir hesap makinesi sıfır veya daha fazla giriş akışı ve/veya taraf alabilir
hiçbir çıkış akışı ve/veya yan paket üretmez.
CalculatorBase
Yeni bir alt sınıf tanımlanarak bir hesap makinesi
CalculatorBase
bir dizi yöntem uygulayarak ve yeni alt sınıfını kullanarak
Mediapipe. Yeni bir hesap makinesi, en azından aşağıdaki dört yöntemi kullanmalıdır
GetContract()
- Hesap makinesi yazarları, beklenen giriş ve çıkış türlerini belirtebilir bir hesap makinesinin karma değerini oluşturun. Bir grafik başlatıldığında, çerçeve, paket türlerinin paket türlerinin doğru olup olmadığını doğrulamak için statik bir yöntem giriş ve çıkışların bu sayfadaki bilgilerle eşleştiğinden, bakın.
Open()
- Grafik başladıktan sonra çerçeve,
Open()
öğesini çağırır. Giriş tarafı hesap makinesinde kullanılabilir hale gelir.Open()
. düğüm yapılandırma işlemlerini yorumlar (bkz. Grafikler) ve hesap makinesinin grafik başına çalıştırma durumunu hazırlar. Bu işlev, hesap makinesi çıkışlarına da paket yazar.Open()
sırasındaki bir hata sonlandırır.
- Grafik başladıktan sonra çerçeve,
Process()
- Girişleri olan bir hesap makinesi için çerçeve art arda
Process()
çağırıyor en az bir giriş akışında paket olduğunda. Çerçeve varsayılan olarak tüm girişlerin aynı zaman damgasına sahip olmasını garanti eder (bkz. Senkronizasyon'u tıklayın). Birden çok Paralel yürütme sırasındaProcess()
aramaları aynı anda başlatılabilir etkin olduğundan emin olun.Process()
sırasında bir hata oluşursa çerçeveClose()
ve grafik çalıştırma sonlandırılır.
- Girişleri olan bir hesap makinesi için çerçeve art arda
Close()
Process()
için yapılan tüm çağrılar tamamlandıktan veya tüm giriş akışları kapandığında çerçeve,Close()
öğesini çağırır. Bu işlev her zamanOpen()
çağrıldı ve başarılı oldu ve grafik çalıştırması sonlandırılsa bile bir hata nedeniyle oluştu. Giriş akışları üzerinden kullanılabilir giriş yokClose()
sırasında ancak giriş tarafı paketlerine ve ve çıkışlar yazabilir.Close()
geri geldikten sonra hesap makinesi ölü düğüm olarak kabul edilmelidir. Hesap makinesi nesnesi yok edildiğinde hemen ardından otomatik olarak gösterilir.
Aşağıdakiler CalculatorBase.h ifadesini kullanın.
class CalculatorBase {
public:
...
// The subclasses of CalculatorBase must implement GetContract.
// ...
static absl::Status GetContract(CalculatorContract* cc);
// Open is called before any Process() calls, on a freshly constructed
// calculator. Subclasses may override this method to perform necessary
// setup, and possibly output Packets and/or set output streams' headers.
// ...
virtual absl::Status Open(CalculatorContext* cc) {
return absl::OkStatus();
}
// Processes the incoming inputs. May call the methods on cc to access
// inputs and produce outputs.
// ...
virtual absl::Status Process(CalculatorContext* cc) = 0;
// Is called if Open() was called and succeeded. Is called either
// immediately after processing is complete or after a graph run has ended
// (if an error occurred in the graph). ...
virtual absl::Status Close(CalculatorContext* cc) {
return absl::OkStatus();
}
...
};
Hesap makinesinin ömrü
Bir MediaPipe grafiğinin başlatılması sırasında çerçeve,
GetContract()
statik yöntemini kullanarak hangi tür paketlerin beklendiğini belirler.
Bu çerçeve, her grafik çalıştırması için hesap makinesinin tamamını oluşturur ve kaldırır (ör. video başına veya resim başına bir defa). Kalan pahalı veya büyük nesneler grafik çalıştırmalarda sabit değer, giriş tarafı paketleri olarak sağlanmalıdır; hesaplamalar sonraki çalıştırmalarda tekrarlanmaz.
Başlatma işleminden sonra, grafiğin her çalışması için aşağıdaki sıra gerçekleşir:
Open()
Process()
(tekrarlanan)Close()
Çerçeve, hesap makinesini başlatmak için Open()
komutunu çağırır. Open()
,
seçenekleri yorumlayın ve hesap makinesinin grafik başına durumunu ayarlayın. Open()
.
giriş tarafı paketleri alabilir ve hesap makinesi çıkışlarına paketler yazabilir. Eğer
olması durumunda, olası paket arabelleğine almayı azaltmak için SetOffset()
komutunu çağırmalıdır
sayısı.
Open()
veya Process()
sırasında bir hata oluşursa (birinin belirttiği şekilde)
Ok
dışında bir durum döndürürse) grafik çalıştırma, başka arama yapılmadan sonlandırılır.
ve hesap makinesi yok olur.
Girişleri olan bir hesap makinesi için çerçeve en az şu zamanlarda Process()
çağırır:
girişlerden birinde kullanılabilir paket var. Bu çerçeve, tüm girişlerin doğru şekilde
aynı zaman damgasına sahip olduğundan zaman damgaları, Process()
çağrısıyla birlikte artar ve
teslim edilir. Sonuç olarak bazı girişlerde
Process()
çağrıldığında gönderilir. Eksik olan bir giriş,
boş bir paket oluşturabilir (zaman damgası olmadan).
Çerçeve, tüm Process()
çağrılarından sonra Close()
öğesini çağırır. Tüm girişler
tükendi, ancak Close()
, giriş tarafı paketlerine erişebilir ve
çıkışlar yazma. Kapat iade edildikten sonra, hesap makinesi yok edilir.
Giriş içermeyen hesap makinelerine kaynak adı verilir. Kaynak hesap makinesi
Ok
durumu döndürdüğü sürece Process()
çağrılmaya devam eder. CEVAP
kaynak hesaplayıcı, bir durdurma durumu döndürdüğünde tükendiğini gösterir
(ör. mediaPipe::tool::StatusStop()
).
Giriş ve çıkışları belirleme
Hesap makinesinin herkese açık arayüzü, bir dizi giriş akışından ve
çıkış akışı sağlar. HesaplayıcıGraphConfiguration'da, oluşturulan bazı
hesap makineleri, adlandırılmış adlar kullanılarak diğer hesap makinelerinin girişlerine bağlanır.
akışlar. Akış adları normalde küçüktür, giriş ve çıkış etiketleri ise
BÜYÜK HARF olmalıdır. Aşağıdaki örnekte, etiket adı VIDEO
olan çıkış
, VIDEO_IN
etiketli girişe, adlı akış kullanılarak bağlandı
video_stream
.
# Graph describing calculator SomeAudioVideoCalculator
node {
calculator: "SomeAudioVideoCalculator"
input_stream: "INPUT:combined_input"
output_stream: "VIDEO:video_stream"
}
node {
calculator: "SomeVideoCalculator"
input_stream: "VIDEO_IN:video_stream"
output_stream: "VIDEO_OUT:processed_video"
}
Giriş ve çıkış akışları dizin numarası, etiket adı veya bir
etiket adı ve dizin numarası kombinasyonundan oluşur. Bazı girdi örneklerini ve
çıkış tanımlayıcılarını kullanın. SomeAudioVideoCalculator
, kendini tanımlar
etiketine göre video çıkışı ve
dizin. VIDEO
etiketine sahip giriş, adlı akışa bağlı
video_stream
. AUDIO
etiketine ve 0
ile 1
dizinlerine sahip çıkışlar şu şekildedir:
audio_left
ve audio_right
adlı akışlara bağlı.
SomeAudioCalculator
, ses girişlerini yalnızca dizine göre tanımlar (etiket gerekmez).
# Graph describing calculator SomeAudioVideoCalculator
node {
calculator: "SomeAudioVideoCalculator"
input_stream: "combined_input"
output_stream: "VIDEO:video_stream"
output_stream: "AUDIO:0:audio_left"
output_stream: "AUDIO:1:audio_right"
}
node {
calculator: "SomeAudioCalculator"
input_stream: "audio_left"
input_stream: "audio_right"
output_stream: "audio_energy"
}
Hesap makinesi uygulamasında, girişler ve çıkışlar etiket tarafından da tanımlanır. ad ve dizin numarası. Aşağıdaki fonksiyonda giriş ve çıkış tanımlanmıştır:
- Dizin numarasına göre: Birleştirilmiş giriş akışı dizin ile tanımlanır
0
- Etiket adına göre: Video çıkış akışı, "VIDEO" etiket adıyla tanımlanır.
- Etiket adına ve dizin numarasına göre: Çıkış ses akışları
AUDIO
etiket adı ile0
ve1
dizin numaralarının kombinasyonudur.
// c++ Code snippet describing the SomeAudioVideoCalculator GetContract() method
class SomeAudioVideoCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->Inputs().Index(0).SetAny();
// SetAny() is used to specify that whatever the type of the
// stream is, it's acceptable. This does not mean that any
// packet is acceptable. Packets in the stream still have a
// particular type. SetAny() has the same effect as explicitly
// setting the type to be the stream's type.
cc->Outputs().Tag("VIDEO").Set<ImageFrame>();
cc->Outputs().Get("AUDIO", 0).Set<Matrix>();
cc->Outputs().Get("AUDIO", 1).Set<Matrix>();
return absl::OkStatus();
}
İşleniyor
Kaynak olmayan bir düğümde çağrılan Process()
, absl::OkStatus()
değerini döndürmelidir
her şeyin iyi gittiğini veya hata sinyalini sağlayan başka bir durum kodunu belirtmek
Kaynak olmayan bir hesap makinesi tool::StatusStop()
sonucunu döndürürse bu
iptal ediliyor. Bu örnekte, tüm kaynak hesaplayıcılar ve grafik
giriş akışları kapatılır (ve kalan Paketler ,
grafiğidir).
Bir grafikteki kaynak düğüm üzerinde Process()
çağrılmaya devam eder
(absl::OkStatus(
döndürdüğünden) emin olun. Yani, bu kitle için
tool::StatusStop()
oranında bir getiri elde etti. Diğer tüm durumlar, bir hatanın olduğunu gösterir
meydana geldi.
Close()
, başarılı olduğunu belirtmek için absl::OkStatus()
değerini döndürür. Diğer durumlar
bir hata olduğunu gösterir.
Temel Process()
işlevi aşağıdaki gibidir. Input()
yöntemini kullanır (
yalnızca hesap makinesinde tek bir giriş varsa) kullanılmalıdır. Google
ardından çıkış paketi için gereken belleği ayırmak üzere std::unique_ptr
politikasını kullanır.
ve hesaplamalar yapar. İşlem tamamlandığında,
çıkış akışı anlamına gelir.
absl::Status MyCalculator::Process() {
const Matrix& input = Input()->Get<Matrix>();
std::unique_ptr<Matrix> output(new Matrix(input.rows(), input.cols()));
// do your magic here....
// output->row(n) = ...
Output()->Add(output.release(), InputTimestamp());
return absl::OkStatus();
}
Hesap makinesi seçenekleri
Hesap makineleri, (1) giriş akışı paketleri (2) aracılığıyla işleme parametrelerini kabul eder
giriş tarafı paketleri ve (3) hesap makinesi seçenekleri. Hesap makinesi seçenekleri (varsa)
değeri belirtilen değerin node_options
alanında değişmez değerler olarak görünür
CalculatorGraphConfiguration.Node
mesaj.
node {
calculator: "TfLiteInferenceCalculator"
input_stream: "TENSORS:main_model_input"
output_stream: "TENSORS:main_model_output"
node_options: {
[type.googleapis.com/mediapipe.TfLiteInferenceCalculatorOptions] {
model_path: "mediapipe/models/detection_model.tflite"
}
}
}
node_options
alanı, proto3 söz dizimini kabul eder. Alternatif olarak, hesap makinesi
seçenekler, proto2 söz dizimi kullanılarak options
alanında belirtilebilir.
node {
calculator: "TfLiteInferenceCalculator"
input_stream: "TENSORS:main_model_input"
output_stream: "TENSORS:main_model_output"
node_options: {
[type.googleapis.com/mediapipe.TfLiteInferenceCalculatorOptions] {
model_path: "mediapipe/models/detection_model.tflite"
}
}
}
Bazı hesap makineleri, hesap makinesi seçeneklerini kabul etmez. Seçenekleri kabul etmek için bir
calculator, normal şartlarda
PacketClonerCalculatorOptions
gibi seçenekler. Hesap makinesi,
bu protobuf mesajını CalculatorBase::Open
yönteminde okuyabilir ve muhtemelen
ayrıca CalculatorBase::GetContract
işlevinde veya
CalculatorBase::Process
yöntemi. Normalde yeni protobuf mesaj türü
".proto" kullanan bir protobuf şeması olarak tanımlanabilir dosya ve bir
mediapipe_proto_library()
derleme kuralı.
mediapipe_proto_library(
name = "packet_cloner_calculator_proto",
srcs = ["packet_cloner_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
],
)
Örnek hesap makinesi
Bu bölümde, PacketClonerCalculator
uygulamak için kullanılan
nispeten basit bir iş yapar ve birçok hesap makinesi grafiğinde kullanılmaktadır.
PacketClonerCalculator
, en son giriş paketlerinin bir kopyasını oluşturur
isteğe bağlı.
PacketClonerCalculator
, gelen veri paketlerinin zaman damgaları olduğunda kullanışlıdır
birbirine tam olarak hizalanmaz. Mikrofonu ve ışıklandırması olan bir odamız olduğunu varsayalım.
sensör ve duyusal veriler toplayan bir video kamera. Sensörlerin her biri
bağımsız olarak çalışır ve aralıklı olarak veri toplar. Diyelim ki çıktı
her bir sensörün boyutu:
- mikrofon = odadaki sesin desibel cinsinden yüksekliği (Tam sayı)
- ışık sensörü = odanın parlaklığı (Tam sayı)
- video kamera = Odanın RGB resim çerçevesi (ImageFrame)
Basit algılama ardışık düzenimiz, bu üç unsurdan alınan duyusal verileri işleyecek şekilde tasarlanmıştır. kameradan alınan resim çerçevesi verileri gibi, böyle bir kameradan toplanan son mikrofon ses düzeyi verisi ve ışığıyla senkronize edilir sensör parlaklık verileri. Bunu MediaPipe ile yapmak için, algı ardışık düzenimizde 3 giriş akışları:
- oda_mikrofonu_signal: Bu giriş akışındaki her veri paketi tam sayıdır odadaki sesin ne kadar yüksek olduğunu zaman damgasıyla gösteren resim.
- Room_lightening_sensor: Bu giriş akışındaki her veri paketi tam sayıdır odanın ne kadar parlak olduğunu zaman damgasıyla gösteren veriler.
- Room_video_tick_signal - Bu giriş akışındaki her bir veri paketi kameradan toplanan videoyu temsil eden video verilerinin resim çerçevesi zaman damgası bulunan bir oda.
Aşağıda PacketClonerCalculator
'ın uygulanması gösterilmektedir. Örneğin
GetContract()
, Open()
ve Process()
yöntemlerinin yanı sıra örneğin
current_
değişkeni en son giriş paketlerini içerir.
// This takes packets from N+1 streams, A_1, A_2, ..., A_N, B.
// For every packet that appears in B, outputs the most recent packet from each
// of the A_i on a separate stream.
#include <vector>
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/calculator_framework.h"
namespace mediapipe {
// For every packet received on the last stream, output the latest packet
// obtained on all other streams. Therefore, if the last stream outputs at a
// higher rate than the others, this effectively clones the packets from the
// other streams to match the last.
//
// Example config:
// node {
// calculator: "PacketClonerCalculator"
// input_stream: "first_base_signal"
// input_stream: "second_base_signal"
// input_stream: "tick_signal"
// output_stream: "cloned_first_base_signal"
// output_stream: "cloned_second_base_signal"
// }
//
class PacketClonerCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
const int tick_signal_index = cc->Inputs().NumEntries() - 1;
// cc->Inputs().NumEntries() returns the number of input streams
// for the PacketClonerCalculator
for (int i = 0; i < tick_signal_index; ++i) {
cc->Inputs().Index(i).SetAny();
// cc->Inputs().Index(i) returns the input stream pointer by index
cc->Outputs().Index(i).SetSameAs(&cc->Inputs().Index(i));
}
cc->Inputs().Index(tick_signal_index).SetAny();
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) final {
tick_signal_index_ = cc->Inputs().NumEntries() - 1;
current_.resize(tick_signal_index_);
// Pass along the header for each stream if present.
for (int i = 0; i < tick_signal_index_; ++i) {
if (!cc->Inputs().Index(i).Header().IsEmpty()) {
cc->Outputs().Index(i).SetHeader(cc->Inputs().Index(i).Header());
// Sets the output stream of index i header to be the same as
// the header for the input stream of index i
}
}
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final {
// Store input signals.
for (int i = 0; i < tick_signal_index_; ++i) {
if (!cc->Inputs().Index(i).Value().IsEmpty()) {
current_[i] = cc->Inputs().Index(i).Value();
}
}
// Output if the tick signal is non-empty.
if (!cc->Inputs().Index(tick_signal_index_).Value().IsEmpty()) {
for (int i = 0; i < tick_signal_index_; ++i) {
if (!current_[i].IsEmpty()) {
cc->Outputs().Index(i).AddPacket(
current_[i].At(cc->InputTimestamp()));
// Add a packet to output stream of index i a packet from inputstream i
// with timestamp common to all present inputs
} else {
cc->Outputs().Index(i).SetNextTimestampBound(
cc->InputTimestamp().NextAllowedInStream());
// if current_[i], 1 packet buffer for input stream i is empty, we will set
// next allowed timestamp for input stream i to be current timestamp + 1
}
}
}
return absl::OkStatus();
}
private:
std::vector<Packet> current_;
int tick_signal_index_;
};
REGISTER_CALCULATOR(PacketClonerCalculator);
} // namespace mediapipe
Genellikle, bir hesap makinesinin yalnızca bir .cc dosyası vardır. .h gerekli değildir, çünkü mediapipe, hesap makinelerini tanıması için kayıttan yararlanır. Bu hesap makinesi sınıfınızı tanımladıysanız, makro çağrısıyla kaydedin REGISTER_CALCULATOR(calculator_class_name).
Aşağıda 3 giriş akışı ve 1 düğüm içeren basit bir MediaPipe grafiği verilmiştir. (PacketClonerCalculator) ve 2 çıkış akışı içeriyor.
input_stream: "room_mic_signal"
input_stream: "room_lighting_sensor"
input_stream: "room_video_tick_signal"
node {
calculator: "PacketClonerCalculator"
input_stream: "room_mic_signal"
input_stream: "room_lighting_sensor"
input_stream: "room_video_tick_signal"
output_stream: "cloned_room_mic_signal"
output_stream: "cloned_lighting_sensor"
}
Aşağıdaki şemada PacketClonerCalculator
öğesinin, çıkışını nasıl tanımladığı gösterilmektedir
paketleri (altta) gösterir.
Paket Cloner Hesaplayıcı, TICK giriş akışında her paket aldığında her bir giriş akışından en son paketi çıkarır. Çıkış paketlerinin sırası (altta), giriş paketlerinin sırasına (üst) ve zaman damgalarına göre belirlenir. Zaman damgaları, şemanın sağ tarafında gösterilir. |