각 계산기는 그래프의 노드입니다. 새 포드를 만드는 방법을
계산기, 계산기 초기화 방법, 계산 방법,
입력 및 출력 스트림, 타임스탬프, 옵션 등이 있습니다. 그래프의 각 노드는
Calculator
로 구현됩니다. 대부분의 그래프 실행은
계산기. 계산기는 0개 이상의 입력 스트림 또는 사이드를 수신할 수 있습니다.
0개 이상의 출력 스트림 또는 부 패킷을 생성합니다.
CalculatorBase
계산기는
CalculatorBase
드림
여러 메서드를 구현하며 새 서브클래스를 등록할 때
Mediapipe. 새 계산기는 최소한 다음 네 가지 방법을 구현해야 합니다.
GetContract()
- 계산기 작성자는 예상되는 입력 및 출력 유형 지정 가능 GetContract()의 계산기를 살펴보세요. 그래프가 초기화되면 프레임워크는 정적 메서드를 호출하여 연결된 입력과 출력은 지정할 수도 있습니다
Open()
- 그래프가 시작된 후 프레임워크는
Open()
를 호출합니다. 입력 측 이 시점에서 계산기에서 사용할 수 있습니다.Open()
노드 구성 작업을 해석합니다 (그래프 참고). 계산기의 그래프 실행별 상태를 준비합니다. 이 함수는 계산기 출력에 패킷을 씁니다.Open()
중에 오류는 그래프 실행을 종료합니다.
- 그래프가 시작된 후 프레임워크는
Process()
- 입력이 있는 계산기의 경우 프레임워크는
Process()
를 반복적으로 호출합니다. 하나 이상의 입력 스트림에 패킷을 사용할 수 있을 때마다. 프레임워크 기본적으로 모든 입력의 타임스탬프가 동일함을 보장합니다( 동기화 참조). 여러 항목 병렬 실행 시Process()
호출을 동시에 호출할 수 있음 사용 설정되어 있는지 확인합니다.Process()
중에 오류가 발생하면 프레임워크는 다음을 호출합니다.Close()
및 그래프 실행이 종료됩니다.
- 입력이 있는 계산기의 경우 프레임워크는
Close()
- 모든
Process()
호출이 완료된 후 또는 모든 입력 스트림이 닫히면 프레임워크는Close()
를 호출합니다. 이 함수는 그래프 실행이 종료된 경우에도Open()
가 호출되어 성공함 오류가 있을 수 있습니다. 입력 스트림을 통해 사용할 수 있는 입력이 없습니다.Close()
동안에도 여전히 입력 측 패킷에 액세스할 수 있으며 출력을 쓸 수 있습니다.Close()
가 반환되면 계산기는 불량 노드로 간주되어야 합니다 계산기 객체는 바로 실행됩니다.
- 모든
다음은 CalculatorBase.h
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();
}
...
};
계산기의 수명
MediaPipe 그래프를 초기화하는 동안 프레임워크에서
필요한 패킷 종류를 결정하는 GetContract()
정적 메서드
프레임워크는 그래프가 실행될 때마다 전체 계산기를 생성하고 폐기합니다. (예: 동영상당 한 번 또는 이미지당 한 번). 비싸거나 큰 객체가 남아 있는 경우 입력 측 패킷으로 제공되어야 하므로 계산은 후속 실행에서 반복되지 않습니다.
초기화 후에는 그래프를 실행할 때마다 다음 순서가 발생합니다.
Open()
Process()
(반복)Close()
프레임워크는 Open()
를 호출하여 계산기를 초기화합니다. Open()
이(가) 해야 하는 작업
모든 옵션을 해석하고 계산기의 그래프 실행별 상태를 설정합니다. Open()
입력 측 패킷을 가져오고 계산기 출력에 패킷을 쓸 수 있습니다. 만약
SetOffset()
를 호출하여 잠재적 패킷 버퍼링을 줄여야 합니다.
학습합니다.
Open()
또는 Process()
중에 오류가 발생하는 경우 (이러한 이벤트 중 하나로 표시됨)
Ok
가 아닌 상태 반환) 더 이상 호출 없이 그래프 실행이 종료됩니다.
계산기가 소멸됩니다.
입력이 있는 계산기의 경우 프레임워크는 적어도 다음의 경우 Process()
를 호출합니다.
한 입력에는 사용 가능한 패킷이 있습니다. 프레임워크는 모든 입력에
동일한 타임스탬프로, Process()
를 호출할 때마다 타임스탬프가 증가합니다.
모든 패킷이 전달되는 것을 의미합니다. 따라서 일부 입력에는
Process()
가 호출될 때 패킷의 개수에 영향을 주지 않습니다. 패킷이 누락된 입력이
빈 패킷 (타임스탬프 없음)을 생성합니다.
프레임워크는 모든 Process()
호출 후에 Close()
를 호출합니다. 모든 입력은
소진되었지만 Close()
는 입력 측 패킷에 액세스할 수 있으며
출력을 씁니다. Close가 반환되면 계산기가 소멸됩니다.
입력이 없는 계산기는 소스라고 합니다. 소스 계산기
Ok
상태를 반환하는 한 Process()
가 계속 호출됩니다. 가
소스 계산기는 중지 상태를 반환하여 소진되었음을 나타냅니다.
(예: mediaPipe::tool::StatusStop()
).
입력 및 출력 식별
계산기의 공개 인터페이스는 일련의 입력 스트림과
출력 스트림. CalculatorGraphConfiguration에서 일부 입력의 출력은
계산기는 이름이 지정된
있습니다. 스트림 이름은 일반적으로 소문자인 반면 입력 및 출력 태그는
대문자여야 합니다. 아래 예에서 태그 이름이 VIDEO
인 출력은 다음과 같습니다.
이름이 VIDEO_IN
인 스트림을 사용하여
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"
}
입력 및 출력 스트림은 색인 번호, 태그 이름 또는
조합될 수 있습니다. 입력의 몇 가지 예시를 살펴보고
출력 식별자를 참조하세요. SomeAudioVideoCalculator
는
태그별 동영상 출력 및 태그별 오디오 출력과
색인 VIDEO
태그가 있는 입력이
video_stream
입니다. 태그가 AUDIO
이고 색인이 0
및 1
인 출력은 다음과 같습니다.
이름이 audio_left
및 audio_right
인 스트림에 연결되었습니다.
SomeAudioCalculator
는 색인으로만 오디오 입력을 식별합니다 (태그 필요 없음).
# 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"
}
계산기 구현에서는 입력과 출력도 태그로 식별됩니다. 색인 번호가 포함됩니다. 아래 함수에서 입력과 출력이 식별됩니다.
- 색인 번호 기준: 결합된 입력 스트림이 색인으로 간단히 식별됩니다.
0
- 태그 이름별: 동영상 출력 스트림은 'VIDEO' 태그 이름으로 식별됩니다.
- 태그 이름 및 색인 번호 기준: 출력 오디오 스트림은
태그 이름
AUDIO
과 색인 번호0
및1
의 조합입니다.
// 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();
}
처리 중
비소스 노드에서 호출된 Process()
는 다음과 같이 absl::OkStatus()
를 반환해야 합니다.
모두 잘 진행되었음을 표시하거나 오류를 알리는 다른 상태 코드를 표시할 수 있습니다.
소스 계산기가 아닌 계산기에서 tool::StatusStop()
를 반환하면
그래프가 일찍 취소됩니다. 이 경우 모든 소스 계산기와 그래프는
입력 스트림이 종료되고 나머지 패킷은
그래프)에서 찾을 수 있습니다.
그래프의 소스 노드는 계속해서 Process()
가 호출되는 동안
absl::OkStatus(
를 반환하므로). 더 이상 표시할 데이터가 없음을 나타내기 위해
반환하면 tool::StatusStop()
이 반환됩니다. 다른 상태는 오류가 있음을 나타냅니다.
수 있습니다.
Close()
는 absl::OkStatus()
를 반환하여 성공을 나타냅니다. 기타 상태
실패를 나타냅니다.
다음은 기본 Process()
함수입니다. Input()
메서드(
는 계산기에 단일 입력이 있는 경우에만 사용) 입력 데이터를 요청합니다. 그것은
그런 다음 std::unique_ptr
를 사용하여 출력 패킷에 필요한 메모리를 할당합니다.
계산을 수행합니다. 완료되면
출력을 생성합니다.
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();
}
계산기 옵션
계산기는 (1) 입력 스트림 패킷 (2)을 통해 처리 매개변수 허용
입력 측 패킷 및 (3) 계산기 옵션 등입니다. 계산기 옵션
지정된 경우 node_options
필드에 리터럴 값으로 표시됩니다.
CalculatorGraphConfiguration.Node
메시지
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
필드는 proto3 구문을 허용합니다. 대신 계산기를
proto2 구문을 사용하여 options
필드에 옵션을 지정할 수 있습니다.
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"
}
}
}
일부 계산기는 계산기 옵션을 사용할 수 없습니다. 옵션을 수락하려면
계산기는 일반적으로 새로운 protobuf 메시지 유형을 정의하여
옵션(예: PacketClonerCalculatorOptions
) 그러면 계산기가
CalculatorBase::Open
메서드에서 protobuf 메시지를 읽고 가능한 경우
CalculatorBase::GetContract
함수 또는
CalculatorBase::Process
메서드를 사용하여 지도 가장자리에
패딩을 추가할 수 있습니다. 일반적으로 새로운 protobuf 메시지 유형은
'.proto'를 사용하여 protobuf 스키마로 정의해야 합니다. 파일 및
mediapipe_proto_library()
빌드 규칙
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",
],
)
계산기 예
이 섹션에서는 PacketClonerCalculator
의 구현에 관해 설명합니다.
비교적 간단한 작업을 수행하며 많은 계산기 그래프에서 사용됩니다.
PacketClonerCalculator
는 단순히 가장 최근 입력 패킷의 사본을 생성합니다.
온디맨드 방식으로 작동합니다
PacketClonerCalculator
는 도착하는 데이터 패킷의 타임스탬프가
완전히 정렬되지 않습니다. 방에 마이크와 조명이 있다고 가정해 봅시다.
센서와 감각 데이터를 수집하는 비디오 카메라가 있습니다. 각각의 센서는
독립적으로 작동하고 간헐적으로 데이터를 수집합니다. 출력이
각 센서의 특징은 다음과 같습니다.
- 마이크 = 방 안의 사운드 크기(데시벨)(정수)
- 광 센서 = 방의 밝기 (정수)
- video camera = 공간의 RGB 이미지 프레임 (ImageFrame)
우리의 단순한 인지 파이프라인은 이 3가지 요소의 감각 데이터를 처리하도록 즉, 카메라의 이미지 프레임 데이터가 있을 때 마지막으로 수집된 마이크 음량 데이터 및 표시등과 동기화됩니다. 센서 밝기 데이터 MediaPipe를 통해 이 작업을 수행하기 위한 인식 파이프라인에는 입력 스트림:
- Room_mic_signal: 이 입력 스트림의 각 데이터 패킷은 정수 데이터입니다. 타임스탬프가 있는 방에서의 오디오 볼륨을 나타냅니다.
- Room_lightening_sensor: 이 입력 스트림의 각 데이터 패킷은 정수입니다. 타임스탬프로 방을 비추는 밝기를 나타내는 데이터입니다.
- Room_video_tick_signal - 이 입력 스트림의 각 데이터 패킷은 카메라에서 수집된 동영상을 나타내는 동영상 데이터의 타임스탬프가 있는 방
다음은 PacketClonerCalculator
의 구현입니다. 이
GetContract()
, Open()
, Process()
메서드 및 인스턴스
변수 current_
- 가장 최근 입력 패킷을 포함합니다.
// 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
일반적으로 계산기에는 .cc 파일만 있습니다. .h가 필요하지 않습니다. mediapipe는 등록을 사용하여 계산기를 알 수 있도록 합니다. 작업을 완료한 후 계산기 클래스를 정의하고 매크로 호출에 등록 REGISTER_CALCULATOR(calculator_class_name).
다음은 3개의 입력 스트림, 1개의 노드가 있는 사소한 MediaPipe 그래프입니다. (PacketClonerCalculator) 및 2개의 출력 스트림.
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"
}
아래 다이어그램은 PacketClonerCalculator
가 출력을 정의하는 방법을 보여줍니다.
일련의 입력 패킷 (위)을 기반으로 하는 패킷 (하단)을 확인할 수 있습니다.
PacketClonerCalculator는 TICK 입력 스트림에서 패킷을 수신할 때마다 각 입력 스트림에서 가장 최근의 패킷을 출력합니다. 출력 패킷의 순서 (아래)는 입력 패킷의 순서 (상단)와 타임스탬프에 따라 결정됩니다. 타임스탬프는 다이어그램 오른쪽에 표시되어 있습니다. |