Project import generated by Copybara.

GitOrigin-RevId: 1e13be30e2c6838d4a2ff768a39c414bc80534bb
This commit is contained in:
MediaPipe Team
2022-09-06 21:46:17 +00:00
committed by Sebastian Schmidt
parent 63e679d99c
commit 4dc4b19ddb
639 changed files with 71327 additions and 2078 deletions
+77
View File
@@ -151,6 +151,16 @@ mediapipe_proto_library(
],
)
mediapipe_proto_library(
name = "get_vector_item_calculator_proto",
srcs = ["get_vector_item_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
],
)
cc_library(
name = "add_header_calculator",
srcs = ["add_header_calculator.cc"],
@@ -561,6 +571,7 @@ cc_test(
name = "packet_cloner_calculator_test",
srcs = ["packet_cloner_calculator_test.cc"],
deps = [
":gate_calculator",
":packet_cloner_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:timestamp",
@@ -1281,6 +1292,7 @@ cc_library(
hdrs = ["get_vector_item_calculator.h"],
visibility = ["//visibility:public"],
deps = [
":get_vector_item_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:packet",
"//mediapipe/framework/api2:node",
@@ -1293,6 +1305,20 @@ cc_library(
alwayslink = 1,
)
cc_test(
name = "get_vector_item_calculator_test",
srcs = ["get_vector_item_calculator_test.cc"],
deps = [
":get_vector_item_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"@com_google_absl//absl/strings:str_format",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "vector_size_calculator",
srcs = ["vector_size_calculator.cc"],
@@ -1307,3 +1333,54 @@ cc_library(
],
alwayslink = 1,
)
cc_library(
name = "packet_sequencer_calculator",
srcs = ["packet_sequencer_calculator.cc"],
visibility = [
"//visibility:public",
],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:contract",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/api2:packet",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/port:status",
"//mediapipe/framework/stream_handler:immediate_input_stream_handler",
],
alwayslink = 1,
)
cc_test(
name = "packet_sequencer_calculator_test",
srcs = ["packet_sequencer_calculator_test.cc"],
deps = [
":packet_sequencer_calculator",
"//mediapipe/calculators/core:pass_through_calculator",
"//mediapipe/framework:calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:subgraph",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "merge_to_vector_calculator",
srcs = ["merge_to_vector_calculator.cc"],
hdrs = ["merge_to_vector_calculator.h"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:image",
"@com_google_absl//absl/status",
],
alwayslink = 1,
)
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message ClipVectorSizeCalculatorOptions {
extend CalculatorOptions {
optional ClipVectorSizeCalculatorOptions ext = 274674998;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message ConcatenateVectorCalculatorOptions {
extend CalculatorOptions {
optional ConcatenateVectorCalculatorOptions ext = 259397839;
@@ -20,8 +20,6 @@ import "mediapipe/framework/calculator.proto";
import "mediapipe/framework/formats/classification.proto";
import "mediapipe/framework/formats/landmark.proto";
option objc_class_prefix = "MediaPipe";
message ConstantSidePacketCalculatorOptions {
extend CalculatorOptions {
optional ConstantSidePacketCalculatorOptions ext = 291214597;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message DequantizeByteArrayCalculatorOptions {
extend CalculatorOptions {
optional DequantizeByteArrayCalculatorOptions ext = 272316343;
@@ -18,7 +18,8 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
option java_package = "com.google.mediapipe.calculator.proto";
option java_outer_classname = "FlowLimiterCalculatorProto";
message FlowLimiterCalculatorOptions {
extend mediapipe.CalculatorOptions {
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message GateCalculatorOptions {
extend mediapipe.CalculatorOptions {
optional GateCalculatorOptions ext = 261754847;
@@ -17,22 +17,24 @@
#include <optional>
#include "mediapipe/calculators/core/get_vector_item_calculator.pb.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
namespace mediapipe {
namespace api2 {
// A calcutlator to return an item from the vector by its index.
// A calculator to return an item from the vector by its index.
// Item index can be specified through INDEX stream and/or calculator options.
// INDEX stream takes precedence over options.
//
// Inputs:
// VECTOR - std::vector<T>
// Vector to take an item from.
// INDEX - int
// INDEX [OPTIONAL] - int
// Index of the item to return.
//
// Outputs:
@@ -45,26 +47,47 @@ namespace api2 {
// input_stream: "VECTOR:vector"
// input_stream: "INDEX:index"
// input_stream: "ITEM:item"
// options {
// [mediapipe.GetVectorItemCalculatorOptions.ext] {
// item_index: 5
// }
// }
// }
//
template <typename T>
class GetVectorItemCalculator : public Node {
public:
static constexpr Input<std::vector<T>> kIn{"VECTOR"};
static constexpr Input<int> kIdx{"INDEX"};
static constexpr Input<int>::Optional kIdx{"INDEX"};
static constexpr Output<T> kOut{"ITEM"};
MEDIAPIPE_NODE_CONTRACT(kIn, kIdx, kOut);
absl::Status Open(CalculatorContext* cc) final {
auto& options = cc->Options<mediapipe::GetVectorItemCalculatorOptions>();
RET_CHECK(kIdx(cc).IsConnected() || options.has_item_index());
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final {
if (kIn(cc).IsEmpty() || kIdx(cc).IsEmpty()) {
if (kIn(cc).IsEmpty()) {
return absl::OkStatus();
}
const std::vector<T>& items = kIn(cc).Get();
const int idx = kIdx(cc).Get();
const auto& options =
cc->Options<mediapipe::GetVectorItemCalculatorOptions>();
RET_CHECK_LT(idx, items.size());
int idx = 0;
if (kIdx(cc).IsConnected() && !kIdx(cc).IsEmpty()) {
idx = kIdx(cc).Get();
} else if (options.has_item_index()) {
idx = options.item_index();
} else {
return absl::OkStatus();
}
RET_CHECK(idx >= 0 && idx < items.size());
kOut(cc).Send(items[idx]);
return absl::OkStatus();
@@ -0,0 +1,29 @@
// Copyright 2022 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message GetVectorItemCalculatorOptions {
extend mediapipe.CalculatorOptions {
optional GetVectorItemCalculatorOptions ext = 463538543;
}
// Index of vector item to get. INDEX input stream can be used instead, or to
// override.
optional int32 item_index = 1;
}
@@ -0,0 +1,230 @@
#include "mediapipe/calculators/core/get_vector_item_calculator.h"
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/str_format.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
MATCHER_P(IntPacket, value, "") {
return testing::Value(arg.template Get<int>(), testing::Eq(value));
}
MATCHER_P(TimestampValue, value, "") {
return testing::Value(arg.Timestamp(), testing::Eq(Timestamp(value)));
}
using TestGetIntVectorItemCalculator = api2::GetVectorItemCalculator<int>;
MEDIAPIPE_REGISTER_NODE(TestGetIntVectorItemCalculator);
CalculatorRunner MakeRunnerWithStream() {
return CalculatorRunner(R"(
calculator: "TestGetIntVectorItemCalculator"
input_stream: "VECTOR:vector_stream"
input_stream: "INDEX:index_stream"
output_stream: "ITEM:item_stream"
)");
}
CalculatorRunner MakeRunnerWithOptions(int set_index) {
return CalculatorRunner(absl::StrFormat(R"(
calculator: "TestGetIntVectorItemCalculator"
input_stream: "VECTOR:vector_stream"
output_stream: "ITEM:item_stream"
options {
[mediapipe.GetVectorItemCalculatorOptions.ext] {
item_index: %d
}
}
)",
set_index));
}
void AddInputVector(CalculatorRunner& runner, const std::vector<int>& inputs,
int timestamp) {
runner.MutableInputs()->Tag("VECTOR").packets.push_back(
MakePacket<std::vector<int>>(inputs).At(Timestamp(timestamp)));
}
void AddInputIndex(CalculatorRunner& runner, int index, int timestamp) {
runner.MutableInputs()->Tag("INDEX").packets.push_back(
MakePacket<int>(index).At(Timestamp(timestamp)));
}
TEST(TestGetIntVectorItemCalculatorTest, EmptyIndexStreamNoOutput) {
CalculatorRunner runner = MakeRunnerWithStream();
const std::vector<int> inputs = {1, 2, 3};
AddInputVector(runner, inputs, 1);
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_EQ(0, outputs.size());
}
TEST(TestGetIntVectorItemCalculatorTest, SuccessfulExtractionIndexStream) {
CalculatorRunner runner = MakeRunnerWithStream();
const std::vector<int> inputs = {1, 2, 3};
const int index = 1;
AddInputVector(runner, inputs, 1);
AddInputIndex(runner, index, 1);
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_THAT(outputs, testing::ElementsAre(IntPacket(inputs[index])));
}
TEST(TestGetIntVectorItemCalculatorTest, SuccessfulExtractionIndexProto) {
const int index = 2;
CalculatorRunner runner = MakeRunnerWithOptions(index);
const std::vector<int> inputs = {1, 2, 3};
AddInputVector(runner, inputs, 1);
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_THAT(outputs, testing::ElementsAre(IntPacket(inputs[index])));
}
TEST(TestGetIntVectorItemCalculatorTest, StreamIsPreferred) {
CalculatorRunner runner(R"(
calculator: "TestGetIntVectorItemCalculator"
input_stream: "VECTOR:vector_stream"
input_stream: "INDEX:index_stream"
output_stream: "ITEM:item_stream"
options {
[mediapipe.GetVectorItemCalculatorOptions.ext] {
item_index: 2
}
}
)");
const std::vector<int> inputs = {1, 2, 3};
const int stream_index = 0;
AddInputVector(runner, inputs, 1);
AddInputIndex(runner, stream_index, 1);
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_THAT(outputs, testing::ElementsAre(IntPacket(inputs[stream_index])));
}
TEST(TestGetIntVectorItemCalculatorTest, NoStreamNorOptionsExpectFail) {
CalculatorRunner runner(R"(
calculator: "TestGetIntVectorItemCalculator"
input_stream: "VECTOR:vector_stream"
output_stream: "ITEM:item_stream"
)");
absl::Status status = runner.Run();
ASSERT_FALSE(status.ok());
EXPECT_THAT(
status.message(),
testing::HasSubstr("kIdx(cc).IsConnected() || options.has_item_index()"));
}
TEST(TestGetIntVectorItemCalculatorTest, StreamIndexBoundsCheckFail1) {
CalculatorRunner runner = MakeRunnerWithStream();
const std::vector<int> inputs = {1, 2, 3};
const int try_index = -1;
AddInputVector(runner, inputs, 1);
AddInputIndex(runner, try_index, 1);
absl::Status status = runner.Run();
ASSERT_FALSE(status.ok());
EXPECT_THAT(status.message(),
testing::HasSubstr("idx >= 0 && idx < items.size()"));
}
TEST(TestGetIntVectorItemCalculatorTest, StreamIndexBoundsCheckFail2) {
CalculatorRunner runner = MakeRunnerWithStream();
const std::vector<int> inputs = {1, 2, 3};
const int try_index = 3;
AddInputVector(runner, inputs, 1);
AddInputIndex(runner, try_index, 1);
absl::Status status = runner.Run();
ASSERT_FALSE(status.ok());
EXPECT_THAT(status.message(),
testing::HasSubstr("idx >= 0 && idx < items.size()"));
}
TEST(TestGetIntVectorItemCalculatorTest, OptionsIndexBoundsCheckFail1) {
const int try_index = -1;
CalculatorRunner runner = MakeRunnerWithOptions(try_index);
const std::vector<int> inputs = {1, 2, 3};
AddInputVector(runner, inputs, 1);
absl::Status status = runner.Run();
ASSERT_FALSE(status.ok());
EXPECT_THAT(status.message(),
testing::HasSubstr("idx >= 0 && idx < items.size()"));
}
TEST(TestGetIntVectorItemCalculatorTest, OptionsIndexBoundsCheckFail2) {
const int try_index = 3;
CalculatorRunner runner = MakeRunnerWithOptions(try_index);
const std::vector<int> inputs = {1, 2, 3};
AddInputVector(runner, inputs, 1);
absl::Status status = runner.Run();
ASSERT_FALSE(status.ok());
EXPECT_THAT(status.message(),
testing::HasSubstr("idx >= 0 && idx < items.size()"));
}
TEST(TestGetIntVectorItemCalculatorTest, IndexStreamTwoTimestamps) {
CalculatorRunner runner = MakeRunnerWithStream();
{
const std::vector<int> inputs = {1, 2, 3};
const int index = 1;
AddInputVector(runner, inputs, 1);
AddInputIndex(runner, index, 1);
}
{
const std::vector<int> inputs = {5, 6, 7, 8};
const int index = 3;
AddInputVector(runner, inputs, 2);
AddInputIndex(runner, index, 2);
}
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_THAT(outputs, testing::ElementsAre(IntPacket(2), IntPacket(8)));
EXPECT_THAT(outputs,
testing::ElementsAre(TimestampValue(1), TimestampValue(2)));
}
TEST(TestGetIntVectorItemCalculatorTest, IndexOptionsTwoTimestamps) {
const int static_index = 2;
CalculatorRunner runner = MakeRunnerWithOptions(static_index);
{
const std::vector<int> inputs = {1, 2, 3};
AddInputVector(runner, inputs, 1);
}
{
const std::vector<int> inputs = {5, 6, 7, 8};
AddInputVector(runner, inputs, 2);
}
MP_ASSERT_OK(runner.Run());
const std::vector<Packet>& outputs = runner.Outputs().Tag("ITEM").packets;
EXPECT_THAT(outputs, testing::ElementsAre(IntPacket(3), IntPacket(7)));
EXPECT_THAT(outputs,
testing::ElementsAre(TimestampValue(1), TimestampValue(2)));
}
} // namespace mediapipe
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message GraphProfileCalculatorOptions {
extend mediapipe.CalculatorOptions {
optional GraphProfileCalculatorOptions ext = 367481815;
@@ -0,0 +1,27 @@
/* Copyright 2022 The MediaPipe Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "mediapipe/calculators/core/merge_to_vector_calculator.h"
#include "mediapipe/framework/formats/image.h"
namespace mediapipe {
namespace api2 {
typedef MergeToVectorCalculator<mediapipe::Image> MergeImagesToVectorCalculator;
MEDIAPIPE_REGISTER_NODE(MergeImagesToVectorCalculator);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,58 @@
/* Copyright 2022 The MediaPipe Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef MEDIAPIPE_CALCULATORS_CORE_MERGE_TO_VECTOR_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_CORE_MERGE_TO_VECTOR_CALCULATOR_H_
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/calculator_framework.h"
namespace mediapipe {
namespace api2 {
template <typename T>
class MergeToVectorCalculator : public Node {
public:
static constexpr typename Input<T>::Multiple kIn{""};
static constexpr Output<std::vector<T>> kOut{""};
MEDIAPIPE_NODE_CONTRACT(kIn, kOut);
static absl::Status UpdateContract(CalculatorContract* cc) {
RET_CHECK_GT(kIn(cc).Count(), 0) << "Needs at least one input stream";
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) {
const int input_num = kIn(cc).Count();
std::vector<T> output_vector(input_num);
std::transform(kIn(cc).begin(), kIn(cc).end(), output_vector.begin(),
[](const auto& elem) -> T { return elem.Get(); });
kOut(cc).Send(output_vector);
return absl::OkStatus();
}
};
} // namespace api2
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_CORE_MERGE_TO_VECTOR_CALCULATOR_H_
@@ -58,6 +58,7 @@ namespace mediapipe {
class PacketClonerCalculator : public CalculatorBase {
public:
static absl::Status GetContract(CalculatorContract* cc) {
cc->SetProcessTimestampBounds(true);
const Ids ids = GetIds(*cc);
for (const auto& in_out : ids.inputs_outputs) {
auto& input = cc->Inputs().Get(in_out.in);
@@ -101,30 +102,30 @@ class PacketClonerCalculator : public CalculatorBase {
}
}
bool has_all_inputs = HasAllInputs();
// Output according to the TICK signal.
if (!cc->Inputs().Get(ids_.tick_id).IsEmpty()) {
if (output_only_when_all_inputs_received_) {
// Return if one of the input is null.
for (int i = 0; i < ids_.inputs_outputs.size(); ++i) {
if (current_[i].IsEmpty()) {
if (output_empty_packets_before_all_inputs_received_) {
SetAllNextTimestampBounds(cc);
}
return absl::OkStatus();
}
}
}
if (!cc->Inputs().Get(ids_.tick_id).IsEmpty() &&
(has_all_inputs || !output_only_when_all_inputs_received_)) {
// Output each stream.
for (int i = 0; i < ids_.inputs_outputs.size(); ++i) {
auto& output = cc->Outputs().Get(ids_.inputs_outputs[i].out);
if (!current_[i].IsEmpty()) {
output.AddPacket(current_[i].At(cc->InputTimestamp()));
} else {
output.SetNextTimestampBound(
cc->InputTimestamp().NextAllowedInStream());
output.AddPacket(current_[i].At(
cc->Inputs().Get(ids_.tick_id).Value().Timestamp()));
}
}
}
// Set timestamp bounds according to the TICK signal.
bool tick_updated = cc->Inputs().Get(ids_.tick_id).Value().Timestamp() ==
cc->InputTimestamp();
bool producing_output = has_all_inputs ||
output_empty_packets_before_all_inputs_received_ ||
!output_only_when_all_inputs_received_;
if (tick_updated && producing_output) {
SetAllNextTimestampBounds(cc);
}
return absl::OkStatus();
}
@@ -165,6 +166,15 @@ class PacketClonerCalculator : public CalculatorBase {
}
}
bool HasAllInputs() {
for (int i = 0; i < ids_.inputs_outputs.size(); ++i) {
if (current_[i].IsEmpty()) {
return false;
}
}
return true;
}
std::vector<Packet> current_;
Ids ids_;
bool output_only_when_all_inputs_received_;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message PacketClonerCalculatorOptions {
extend CalculatorOptions {
optional PacketClonerCalculatorOptions ext = 258872085;
@@ -33,6 +33,7 @@ namespace {
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::IsTrue;
using ::testing::Value;
MATCHER_P2(IntPacket, value, ts, "") {
@@ -45,6 +46,11 @@ MATCHER_P2(FloatPacket, value, ts, "") {
Value(arg.Timestamp(), Eq(Timestamp(ts)));
}
MATCHER_P(EmptyPacket, ts, "") {
return Value(arg.IsEmpty(), IsTrue()) &&
Value(arg.Timestamp(), Eq(Timestamp(ts)));
}
template <typename T>
absl::Status SendPacket(const std::string& input_name, T value, int ts,
CalculatorGraph& graph) {
@@ -342,6 +348,105 @@ TEST_P(PacketClonerCalculatorTest,
FloatPacket(40.0f, 40000))));
}
class PacketClonerCalculatorGatedInputTest : public ::testing::Test {
protected:
void SetUp() override {
CalculatorGraphConfig graph_config =
ParseTextProtoOrDie<CalculatorGraphConfig>([&]() {
return R"pb(
input_stream: 'input'
input_stream: 'input_enabled'
input_stream: 'tick'
input_stream: 'tick_enabled'
node {
calculator: 'GateCalculator'
input_stream: 'tick'
input_stream: 'ALLOW:tick_enabled'
output_stream: 'tick_gated'
}
node {
calculator: 'GateCalculator'
input_stream: 'input'
input_stream: 'ALLOW:input_enabled'
output_stream: 'input_gated'
}
node {
calculator: 'PacketClonerCalculator'
input_stream: 'input_gated'
input_stream: 'TICK:tick_gated'
output_stream: 'output'
})pb";
}());
MP_ASSERT_OK(graph.Initialize(graph_config, {}));
MP_ASSERT_OK(graph.ObserveOutputStream(
"output",
[this](Packet const& packet) {
output.push_back(packet);
return absl::OkStatus();
},
true));
MP_ASSERT_OK(graph.StartRun({}));
}
CalculatorGraph graph;
std::vector<Packet> output;
};
TEST_F(PacketClonerCalculatorGatedInputTest,
PropagatesTimestampBoundsWithEmptyInput) {
MP_ASSERT_OK(SendPacket("tick_enabled", false, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick", 0, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("input_enabled", false, /*ts=*/200, graph));
MP_ASSERT_OK(SendPacket("input", 1, /*ts=*/200, graph));
MP_ASSERT_OK(graph.WaitUntilIdle());
EXPECT_THAT(output, ElementsAre(EmptyPacket(100)));
}
TEST_F(PacketClonerCalculatorGatedInputTest,
PropagatesTimestampBoundsWithInput) {
MP_ASSERT_OK(SendPacket("input_enabled", true, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("input", 1, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick_enabled", true, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick", 0, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick_enabled", false, /*ts=*/110, graph));
MP_ASSERT_OK(SendPacket("tick", 0, /*ts=*/110, graph));
MP_ASSERT_OK(SendPacket("input_enabled", false, /*ts=*/200, graph));
MP_ASSERT_OK(SendPacket("input", 2, /*ts=*/200, graph));
MP_ASSERT_OK(graph.WaitUntilIdle());
EXPECT_THAT(output, ElementsAre(IntPacket(1, 100), EmptyPacket(110)));
}
TEST_F(PacketClonerCalculatorGatedInputTest,
PropagatesTimestampBoundsFromTick) {
MP_ASSERT_OK(SendPacket("input_enabled", true, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("input", 1, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick_enabled", true, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("tick", 0, /*ts=*/100, graph));
MP_ASSERT_OK(SendPacket("input_enabled", true, /*ts=*/110, graph));
MP_ASSERT_OK(SendPacket("input", 2, /*ts=*/110, graph));
MP_ASSERT_OK(SendPacket("tick_enabled", false, /*ts=*/200, graph));
MP_ASSERT_OK(SendPacket("tick", 0, /*ts=*/200, graph));
MP_ASSERT_OK(SendPacket("input_enabled", false, /*ts=*/200, graph));
MP_ASSERT_OK(SendPacket("input", 2, /*ts=*/200, graph));
MP_ASSERT_OK(graph.WaitUntilIdle());
EXPECT_THAT(output, ElementsAre(IntPacket(1, 100), EmptyPacket(200)));
}
INSTANTIATE_TEST_SUITE_P(PacketClonerCalculator, PacketClonerCalculatorTest,
testing::ValuesIn({Params{.use_tick_tag = false},
Params{.use_tick_tag = true}}));
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message PacketResamplerCalculatorOptions {
extend CalculatorOptions {
optional PacketResamplerCalculatorOptions ext = 95743844;
@@ -0,0 +1,103 @@
// Copyright 2022 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <memory>
#include "mediapipe/framework/api2/contract.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/api2/packet.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/status.h"
namespace mediapipe {
namespace api2 {
// This calculator assigns a timestamp to each "INPUT" packet reflecting
// the most recent "TICK" timestamp.
//
// Each "TICK" timestamp is propagated as a settled "OUTPUT" timestamp.
// This allows "TICK" packets to be processed right away.
// When an "INPUT" packet arrives, it is sent to the "OUTPUT" stream with
// the next unsettled "OUTPUT" timestamp, which is normally one greater than
// the most recent "TICK" timestamp.
//
// If a "TICK" packet and an "INPUT" packet arrive together, the "OUTPUT"
// packet timestamp is derived from the previous "TICK" timestamp,
// and the new "OUTPUT" bound is derived from the current "TICK" timestamp.
// This allows the current "INPUT" packet to cover the current "TICK" timestamp.
//
// Example config:
// node {
// calculator: "PacketSequencerCalculator"
// input_stream: "INPUT:switch_selection"
// input_stream: "TICK:input_image"
// input_stream: "TICK:input_audio"
// output_stream: "OUTPUT:switch_selection_timed"
// }
//
class PacketSequencerCalculator : public Node {
public:
static constexpr Input<AnyType>::Multiple kInput{"INPUT"};
static constexpr Input<AnyType>::Multiple kTick{"TICK"};
static constexpr Output<AnyType>::Multiple kOutput{"OUTPUT"};
MEDIAPIPE_NODE_CONTRACT(kInput, kTick, kOutput,
StreamHandler("ImmediateInputStreamHandler"),
TimestampChange::Arbitrary());
static absl::Status UpdateContract(CalculatorContract* cc) {
RET_CHECK_EQ(kInput(cc).Count(), kOutput(cc).Count());
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) final {
// Pass through any input packets at the output stream bound.
for (int i = 0; i < kInput(cc).Count(); ++i) {
Timestamp stream_bound = kOutput(cc)[i].NextTimestampBound();
const PacketBase input_packet = kInput(cc)[i].packet();
if (!input_packet.IsEmpty()) {
Timestamp output_ts = std::max(Timestamp::Min(), stream_bound);
kOutput(cc)[i].Send(input_packet.At(output_ts));
}
}
// Find the new tick timestamp, if any.
Timestamp tick_ts = Timestamp::Min();
for (int i = 0; i < kTick(cc).Count(); ++i) {
const PacketBase& tick_packet = kTick(cc)[i].packet();
// For either an input packet or an empty input stream,
// the packet timestamp indicates the latest "settled timestamp",
// and when it arrives it equals the InputTimestamp().
if (tick_packet.timestamp() == cc->InputTimestamp()) {
tick_ts = std::max(tick_ts, tick_packet.timestamp());
break;
}
}
// Advance all output stream bounds past the tick timestamp.
for (int i = 0; i < kInput(cc).Count(); ++i) {
Timestamp stream_bound = kOutput(cc)[i].NextTimestampBound();
if (tick_ts >= stream_bound) {
kOutput(cc)[i].SetNextTimestampBound(tick_ts.NextAllowedInStream());
}
}
return absl::OkStatus();
}
};
MEDIAPIPE_REGISTER_NODE(PacketSequencerCalculator);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,118 @@
// Copyright 2022 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "mediapipe/framework/calculator.pb.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
namespace {
// Returns a CalculatorGraph to run a single calculator.
CalculatorGraph BuildCalculatorGraph(CalculatorGraphConfig::Node node_config) {
CalculatorGraphConfig config;
*config.add_node() = node_config;
*config.mutable_input_stream() = node_config.input_stream();
*config.mutable_output_stream() = node_config.output_stream();
*config.mutable_input_side_packet() = node_config.input_side_packet();
*config.mutable_output_side_packet() = node_config.output_side_packet();
return CalculatorGraph(config);
}
// Creates a string packet.
Packet pack(std::string data, int timestamp) {
return MakePacket<std::string>(data).At(Timestamp(timestamp));
}
// Tests showing packet timestamp synchronization through
// PacketSequencerCalculator.
class PacketSequencerCalculatorTest : public ::testing::Test {
protected:
PacketSequencerCalculatorTest() {}
~PacketSequencerCalculatorTest() override {}
void SetUp() override {}
void TearDown() override {}
// Defines a PacketSequencerCalculator CalculatorGraphConfig::Node.
CalculatorGraphConfig::Node BuildNodeConfig() {
CalculatorGraphConfig::Node result;
*result.mutable_calculator() = "PacketSequencerCalculator";
*result.add_input_stream() = "INPUT:select";
*result.add_input_stream() = "TICK:0:frame";
*result.add_input_stream() = "TICK:1:mask";
*result.add_output_stream() = "OUTPUT:select_timed";
return result;
}
};
// Shows the PacketSequencerCalculator is available.
TEST_F(PacketSequencerCalculatorTest, IsRegistered) {
EXPECT_TRUE(
CalculatorBaseRegistry::IsRegistered("PacketSequencerCalculator"));
}
// Shows how control packets recieve timestamps before and after frame packets
// have arrived.
TEST_F(PacketSequencerCalculatorTest, ChannelEarly) {
CalculatorGraphConfig::Node node_config = BuildNodeConfig();
CalculatorGraph graph = BuildCalculatorGraph(node_config);
std::vector<Packet> outputs;
MP_ASSERT_OK(graph.ObserveOutputStream("select_timed", [&](const Packet& p) {
outputs.push_back(p);
return absl::OkStatus();
}));
MP_ASSERT_OK(graph.StartRun({}));
// Some control packets arrive.
MP_ASSERT_OK(graph.AddPacketToInputStream("select", pack("p0_t10", 10)));
MP_ASSERT_OK(graph.AddPacketToInputStream("select", pack("p0_t20", 20)));
MP_ASSERT_OK(graph.WaitUntilIdle());
// The control packets are assigned low timestamps.
ASSERT_EQ(outputs.size(), 2);
EXPECT_EQ(outputs[0].Get<std::string>(), "p0_t10");
EXPECT_EQ(outputs[0].Timestamp(), Timestamp::Min());
EXPECT_EQ(outputs[1].Timestamp(), Timestamp::Min() + 1);
// Some frame packets arrive.
MP_ASSERT_OK(graph.AddPacketToInputStream("mask", pack("p2_t10", 10)));
MP_ASSERT_OK(graph.AddPacketToInputStream("frame", pack("p1_t20", 20)));
MP_ASSERT_OK(graph.WaitUntilIdle());
// Some more control packets arrive.
MP_ASSERT_OK(graph.AddPacketToInputStream("select", pack("p0_t30", 30)));
MP_ASSERT_OK(graph.AddPacketToInputStream("select", pack("p0_t40", 40)));
MP_ASSERT_OK(graph.WaitUntilIdle());
// New control packets are assigned timestamps following Timestamp(20).
ASSERT_EQ(outputs.size(), 4);
EXPECT_EQ(outputs[2].Get<std::string>(), "p0_t30");
EXPECT_EQ(outputs[2].Timestamp(), Timestamp(21));
EXPECT_EQ(outputs[3].Timestamp(), Timestamp(22));
MP_ASSERT_OK(graph.CloseAllPacketSources());
MP_ASSERT_OK(graph.WaitUntilDone());
}
} // namespace
} // namespace mediapipe
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message PacketThinnerCalculatorOptions {
extend CalculatorOptions {
optional PacketThinnerCalculatorOptions ext = 288533508;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message QuantizeFloatVectorCalculatorOptions {
extend CalculatorOptions {
optional QuantizeFloatVectorCalculatorOptions ext = 259848061;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
message SequenceShiftCalculatorOptions {
extend CalculatorOptions {
optional SequenceShiftCalculatorOptions ext = 107633927;
@@ -18,8 +18,6 @@ package mediapipe;
import "mediapipe/framework/calculator.proto";
option objc_class_prefix = "MediaPipe";
// A Range {begin, end} specifies beginning ane ending indices to splice a
// vector. A vector v is spliced to have elements v[begin:(end-1)], i.e., with
// begin index inclusive and end index exclusive.