Biểu đồ
Proto CalculatorGraphConfig
chỉ định cấu trúc liên kết và chức năng của một
Biểu đồ MediaPipe. Mỗi node
trong biểu đồ đại diện cho một máy tính cụ thể hoặc
đồ thị con và chỉ định các cấu hình cần thiết, chẳng hạn như các cấu hình đã đăng ký
loại máy tính/đồ thị con, dữ liệu đầu vào, dữ liệu đầu ra và các trường tuỳ chọn, chẳng hạn như
các tùy chọn cụ thể theo nút, chính sách đầu vào và bộ thực thi, được thảo luận trong
Đồng bộ hoá.
CalculatorGraphConfig
có một số trường khác để định cấu hình cấp biểu đồ toàn cầu
cài đặt, ví dụ: cấu hình trình thực thi biểu đồ, số lượng luồng và kích thước hàng đợi tối đa
của luồng đầu vào. Một số cài đặt cấp biểu đồ rất hữu ích trong việc điều chỉnh
hiệu suất của biểu đồ trên các nền tảng khác nhau (ví dụ: máy tính để bàn so với thiết bị di động). Cho
chẳng hạn như trên thiết bị di động, việc gắn một máy tính suy luận mô hình nặng vào một máy tính
executor có thể cải thiện hiệu suất của ứng dụng theo thời gian thực vì đây là
cho phép xác định vị trí của luồng.
Dưới đây là một ví dụ quan trọng về CalculatorGraphConfig
, trong đó chúng ta có một loạt
công cụ tính giá trị chuyển nhượng :
# 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 cung cấp cách biểu diễn C++
thay thế cho các biểu đồ phức tạp (ví dụ: quy trình học máy, siêu dữ liệu mô hình xử lý, các nút không bắt buộc, v.v.). Biểu đồ trên có thể hiển thị như sau:
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();
}
Xem thêm thông tin chi tiết trong bài viết Xây dựng biểu đồ trong C++.
Đồ thị con
Để mô-đun hoá CalculatorGraphConfig
thành các mô-đun con và hỗ trợ sử dụng lại
về giải pháp nhận thức, biểu đồ MediaPipe có thể được định nghĩa là một Subgraph
. Chiến lược phát hành đĩa đơn
giao diện công khai của một đồ thị con bao gồm tập hợp các luồng đầu vào và đầu ra
tương tự như giao diện công khai của một máy tính. Sau đó, đồ thị con có thể được đưa vào
CalculatorGraphConfig
giống như một máy tính. Khi biểu đồ MediaPipe là
được tải từ CalculatorGraphConfig
, mỗi nút đồ thị con sẽ được thay thế bằng
đồ thị tương ứng của máy tính. Do đó, ngữ nghĩa và hiệu suất
của đồ thị con giống hệt với đồ thị tương ứng của máy tính.
Dưới đây là ví dụ về cách tạo đồ thị con có tên TwoPassThroughSubgraph
.
Xác định đồ thị con.
# 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" }
Giao diện công khai của đồ thị con bao gồm:
- Vẽ biểu đồ luồng đầu vào
- Vẽ đồ thị các luồng đầu ra
- Vẽ đồ thị gói bên đầu vào
- Vẽ đồ thị các gói bên đầu ra
Đăng ký đồ thị con bằng cách sử dụng quy tắc BUILD
mediapipe_simple_subgraph
. Chiến lược phát hành đĩa đơn tham sốregister_as
định nghĩa tên thành phần cho đồ thị con mới.# 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", ], )
Sử dụng đồ thị con trong biểu đồ chính.
# 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" }
Tùy chọn biểu đồ
Có thể chỉ định "tuỳ chọn biểu đồ" protobuf cho đồ thị MediaPipe
tương tự như Calculator Options
protobuf được chỉ định cho máy tính MediaPipe. Các "tuỳ chọn biểu đồ" này có thể
được chỉ định tại vị trí gọi biểu đồ và được sử dụng để điền các tuỳ chọn tính toán và
tuỳ chọn đồ thị con trong biểu đồ.
Trong CalculatorGraphConfig
, bạn có thể chỉ định các tuỳ chọn biểu đồ cho một đồ thị con
giống hệt như các tuỳ chọn máy tính, như được trình bày dưới đây:
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
}
}
}
Trong CalculatorGraphConfig
, các tuỳ chọn trong biểu đồ có thể được chấp nhận và dùng để điền dữ liệu
tuỳ chọn máy tính, như được hiển thị dưới đây:
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"
}
Trong ví dụ này, FaceDetectionSubgraph
chấp nhận protobuf tuỳ chọn biểu đồ
FaceDetectionOptions
FaceDetectionOptions
dùng để xác định một số trường
các giá trị trong tuỳ chọn máy tính ImageToTensorCalculatorOptions
và một số trường
trong các tuỳ chọn của đồ thị con InferenceCalculatorOptions
. Các giá trị trường
được xác định bằng cú pháp option_value:
.
Trong protobuf CalculatorGraphConfig::Node
, các trường node_options:
và
option_value:
cùng xác định các giá trị tuỳ chọn cho một hàm tính, chẳng hạn như
ImageToTensorCalculator
. Trường node_options:
xác định một tập hợp các giá trị cố định
bằng cách sử dụng cú pháp protobuf văn bản. Mỗi trường option_value:
xác định giá trị cho một trường protobuf bằng cách sử dụng thông tin từ trường bao gồm
biểu đồ, cụ thể là từ các giá trị trường của các tuỳ chọn biểu đồ bao quanh
biểu đồ. Trong ví dụ trên, option_value:
"output_tensor_width:options/tensor_width"
xác định trường
ImageToTensorCalculatorOptions.output_tensor_width
bằng cách sử dụng giá trị của
FaceDetectionOptions.tensor_width
.
Cú pháp của option_value:
tương tự như cú pháp của input_stream:
. Chiến lược phát hành đĩa đơn
là option_value: "LHS:RHS"
. LHS xác định một lựa chọn tính toán
và RHS xác định một trường tuỳ chọn trên biểu đồ. Cụ thể hơn, LHS
và RHS đều bao gồm một loạt tên trường protobuf để xác định các tên được lồng vào nhau
các trường và thông báo protobuf được phân tách bằng dấu "/". Đây được gọi là "ProtoPath"
của bạn. Các thông báo lồng nhau được tham chiếu trong LHS hoặc RHS phải
được xác định trong protobuf kèm theo để có thể truyền tải bằng cách sử dụng
option_value:
.
Bốn mùa
Theo mặc định, MediaPipe yêu cầu biểu đồ tính toán phải có tính chu kỳ và xử lý các chu kỳ trong biểu đồ dưới dạng lỗi. Nếu biểu đồ dự định có chu kỳ, các chu kỳ cần phải được chú thích trong cấu hình biểu đồ. Trang này sẽ mô tả cách thực hiện việc đó.
LƯU Ý: Phương pháp hiện tại đang trong giai đoạn thử nghiệm và có thể thay đổi. Chúng tôi rất vui được hỗ trợ bạn ý kiến phản hồi của bạn.
Vui lòng sử dụng kiểm thử đơn vị CalculatorGraphTest.Cycle
trong
mediapipe/framework/calculator_graph_test.cc
làm mã mẫu. Hiển thị bên dưới là
đồ thị tuần hoàn trong phép kiểm thử. Đầu ra sum
của bộ cộng là tổng của
số nguyên được tạo bởi máy tính nguồn số nguyên.
Biểu đồ đơn giản này minh hoạ tất cả các vấn đề trong việc hỗ trợ đồ thị tuần hoàn.
Chú thích ở cạnh sau
Chúng tôi yêu cầu một cạnh trong mỗi chu kỳ phải được chú thích là cạnh sau. Điều này cho phép Phương thức sắp xếp cấu trúc liên kết của MediaPipe hoạt động, sau khi loại bỏ tất cả các cạnh sau.
Thông thường, có nhiều cách để chọn cạnh sau. Những cạnh nào được đánh dấu vì cạnh sau ảnh hưởng đến nút nào được coi là thượng nguồn và nút nào được coi là hạ nguồn, từ đó ảnh hưởng đến mức độ ưu tiên mà MediaPipe chỉ định vào các nút.
Ví dụ: phép kiểm thử CalculatorGraphTest.Cycle
đánh dấu cạnh old_sum
là một
cạnh sau, nên nút Delay được coi là một nút hạ nguồn của bộ cộng
nút và được gán mức độ ưu tiên cao hơn. Ngoài ra, chúng ta có thể đánh dấu sum
đầu vào vào nút trễ làm cạnh sau, trong trường hợp này, nút trễ sẽ là
được coi là nút ngược dòng của nút bộ cộng và có mức độ ưu tiên thấp hơn.
Gói tin ban đầu
Để hàm tính bộ cộng có thể chạy được khi số nguyên đầu tiên trong số nguyên đó
nguồn đến, chúng ta cần một gói ban đầu có giá trị 0 và có cùng
dấu thời gian, trên luồng đầu vào old_sum
đến trình thêm. Gói ban đầu này
phải được xuất ra bằng máy tính độ trễ trong phương thức Open()
.
Độ trễ trong một vòng lặp
Mỗi vòng lặp phải có độ trễ để căn chỉnh đầu ra sum
trước đó với đầu ra tiếp theo
đầu vào là số nguyên. Việc này cũng được thực hiện bằng nút trễ. Vì vậy, nút trễ cần phải
hãy xem những thông tin sau đây về dấu thời gian của hàm tính nguồn số nguyên:
Dấu thời gian của lần xuất đầu tiên.
delta dấu thời gian giữa các dữ liệu đầu ra liên tiếp.
Chúng tôi dự định thêm một chính sách lập lịch thay thế chỉ quan tâm đến gói dữ liệu sắp xếp thứ tự và bỏ qua dấu thời gian của gói. Điều này giúp loại bỏ sự bất tiện này.
Chấm dứt sớm một máy tính khi hoàn tất một luồng đầu vào
Theo mặc định, MediaPipe gọi phương thức Close()
của một công cụ tính không phải nguồn khi
tất cả các luồng đầu vào đã hoàn tất. Trong biểu đồ ví dụ, chúng tôi muốn dừng
nút bộ cộng ngay khi nguồn số nguyên hoàn tất. Điều này được thực hiện bằng
định cấu hình nút bộ cộng bằng một trình xử lý luồng đầu vào thay thế,
EarlyCloseInputStreamHandler
.
Mã nguồn có liên quan
Công cụ tính độ trễ
Lưu ý mã trong Open()
xuất ra gói tin ban đầu và mã trong
Process()
thêm độ trễ (đơn vị) cho các gói đầu vào. Như đã lưu ý ở trên, đây là
nút trễ giả định rằng luồng đầu ra của nó được dùng cùng với luồng đầu vào có
dấu thời gian gói tin 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();
}
};
Cấu hình biểu đồ
Hãy lưu ý chú giải back_edge
và input_stream_handler
thay thế.
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'
}