Project import generated by Copybara.

GitOrigin-RevId: 947096e4fc99d6b974e9f50d360d7c0a75072c5d
This commit is contained in:
MediaPipe Team 2020-09-17 11:43:21 -04:00 committed by chuoling
parent a908d668c7
commit 8f69af91fe
8 changed files with 159 additions and 35 deletions

View File

@ -50,6 +50,10 @@ Object Detection
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | | [MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | | [YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
See also
[MediaPipe Models and Model Cards](https://google.github.io/mediapipe/solutions/models)
for ML models released in MediaPipe.
## MediaPipe on the Web ## MediaPipe on the Web
MediaPipe on the Web is an effort to run the same ML solutions built for mobile MediaPipe on the Web is an effort to run the same ML solutions built for mobile
@ -89,7 +93,8 @@ run code search using
## Publications ## Publications
* [Face AR with MediaPipe Face Mesh](https://mediapipe.page.link/face-geometry-blog) in Google Developers Blog * [MediaPipe 3D Face Transform](https://mediapipe.page.link/face-geometry-blog)
in Google Developers Blog
* [Instant Motion Tracking With MediaPipe](https://developers.googleblog.com/2020/08/instant-motion-tracking-with-mediapipe.html) * [Instant Motion Tracking With MediaPipe](https://developers.googleblog.com/2020/08/instant-motion-tracking-with-mediapipe.html)
in Google Developers Blog in Google Developers Blog
* [BlazePose - On-device Real-time Body Pose Tracking](https://ai.googleblog.com/2020/08/on-device-real-time-body-pose-tracking.html) * [BlazePose - On-device Real-time Body Pose Tracking](https://ai.googleblog.com/2020/08/on-device-real-time-body-pose-tracking.html)

View File

@ -50,6 +50,10 @@ Object Detection
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | | [MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | | [YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
See also
[MediaPipe Models and Model Cards](https://google.github.io/mediapipe/solutions/models)
for ML models released in MediaPipe.
## MediaPipe on the Web ## MediaPipe on the Web
MediaPipe on the Web is an effort to run the same ML solutions built for mobile MediaPipe on the Web is an effort to run the same ML solutions built for mobile
@ -89,7 +93,8 @@ run code search using
## Publications ## Publications
* [Face AR with MediaPipe Face Mesh](https://mediapipe.page.link/face-geometry-blog) in Google Developers Blog * [MediaPipe 3D Face Transform](https://mediapipe.page.link/face-geometry-blog)
in Google Developers Blog
* [Instant Motion Tracking With MediaPipe](https://developers.googleblog.com/2020/08/instant-motion-tracking-with-mediapipe.html) * [Instant Motion Tracking With MediaPipe](https://developers.googleblog.com/2020/08/instant-motion-tracking-with-mediapipe.html)
in Google Developers Blog in Google Developers Blog
* [BlazePose - On-device Real-time Body Pose Tracking](https://ai.googleblog.com/2020/08/on-device-real-time-body-pose-tracking.html) * [BlazePose - On-device Real-time Body Pose Tracking](https://ai.googleblog.com/2020/08/on-device-real-time-body-pose-tracking.html)

View File

@ -277,7 +277,7 @@ only works for a single face. For visual reference, please refer to *Fig. 4*.
* TensorFlow Blog: * TensorFlow Blog:
[Face and hand tracking in the browser with MediaPipe and TensorFlow.js](https://blog.tensorflow.org/2020/03/face-and-hand-tracking-in-browser-with-mediapipe-and-tensorflowjs.html) [Face and hand tracking in the browser with MediaPipe and TensorFlow.js](https://blog.tensorflow.org/2020/03/face-and-hand-tracking-in-browser-with-mediapipe-and-tensorflowjs.html)
* Google Developers Blog: * Google Developers Blog:
[Face AR with MediaPipe Face Mesh](https://mediapipe.page.link/face-geometry-blog) [MediaPipe 3D Face Transform](https://mediapipe.page.link/face-geometry-blog)
* Paper: * Paper:
[Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs](https://arxiv.org/abs/1907.06724) [Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs](https://arxiv.org/abs/1907.06724)
([poster](https://docs.google.com/presentation/d/1-LWwOMO9TzEVdrZ1CS1ndJzciRHfYDJfbSxH_ke_JRg/present?slide=id.g5986dd4b4c_4_212)) ([poster](https://docs.google.com/presentation/d/1-LWwOMO9TzEVdrZ1CS1ndJzciRHfYDJfbSxH_ke_JRg/present?slide=id.g5986dd4b4c_4_212))

View File

@ -5,7 +5,7 @@ parent: Solutions
nav_order: 30 nav_order: 30
--- ---
# Models and Model Cards # MediaPipe Models and Model Cards
{: .no_toc } {: .no_toc }
1. TOC 1. TOC

View File

@ -32,3 +32,7 @@ has_toc: false
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | | | [AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | | |
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | | [MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | | [YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
See also
[MediaPipe Models and Model Cards](https://google.github.io/mediapipe/solutions/models)
for ML models released in MediaPipe.

View File

@ -42,16 +42,16 @@ namespace tf = tensorflow;
// a flag controls whether a new first dimension is inserted before // a flag controls whether a new first dimension is inserted before
// concatenation. // concatenation.
// //
// Currently, the number of tensors output will be buffer_size less than the // The number of tensors output will be buffer_size less than the
// number of input tensors because no padding is implemented and only full // number of input tensors unless padding is set to a non-zero value in the
// buffers are output. // options proto.
// //
// The timestamp of the output batch will match the timestamp of the first // The timestamp of the output batch will match the timestamp of the first
// tensor in that batch by default. (e.g. when buffer_size frames are added, the // tensor in that batch by default. (e.g. when buffer_size frames are added, the
// output tensor will have the timestamp of the first input.). This behavior can // output tensor will have the timestamp of the first input.). This behavior can
// be adjusted by the timestamp_offset option. // be adjusted by the timestamp_offset option.
// //
// Example config: // Example config without padding:
// node { // node {
// calculator: "LappedTensorBufferCalculator" // calculator: "LappedTensorBufferCalculator"
// input_stream: "input_tensor" // input_stream: "input_tensor"
@ -64,26 +64,50 @@ namespace tf = tensorflow;
// } // }
// } // }
// } // }
//
// Example config with padding and timestamp output:
// node {
// calculator: "LappedTensorBufferCalculator"
// input_stream: "input_tensor"
// output_stream: "output_tensor"
// output_stream: "output_timestamp"
// options {
// [mediapipe.LappedTensorBufferCalculatorOptions.ext] {
// buffer_size: 100
// overlap: 50
// add_batch_dim_to_tensors: true
// timestamp_offset: 25
// padding: 25
// }
// }
// }
class LappedTensorBufferCalculator : public CalculatorBase { class LappedTensorBufferCalculator : public CalculatorBase {
public: public:
static ::mediapipe::Status GetContract(CalculatorContract* cc); static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override; ::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override; ::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
private: private:
// Adds a batch dimension to the input tensor if specified in the calculator // Adds a batch dimension to the input tensor if specified in the
// options. // calculator options.
::mediapipe::Status AddBatchDimension(tf::Tensor* input_tensor); ::mediapipe::Status AddBatchDimension(tf::Tensor* input_tensor);
// Sends the current buffer downstream.
::mediapipe::Status ProcessBuffer(CalculatorContext* cc);
int steps_until_output_; int steps_until_output_;
int buffer_size_; int buffer_size_;
int overlap_; int overlap_;
int timestamp_offset_; int timestamp_offset_;
int initialized_;
std::unique_ptr<CircularBuffer<Timestamp>> timestamp_buffer_; std::unique_ptr<CircularBuffer<Timestamp>> timestamp_buffer_;
std::unique_ptr<CircularBuffer<tf::Tensor>> buffer_; std::unique_ptr<CircularBuffer<tf::Tensor>> buffer_;
LappedTensorBufferCalculatorOptions options_; LappedTensorBufferCalculatorOptions options_;
}; };
REGISTER_CALCULATOR(LappedTensorBufferCalculator); REGISTER_CALCULATOR(LappedTensorBufferCalculator);
::mediapipe::Status LappedTensorBufferCalculator::GetContract( ::mediapipe::Status LappedTensorBufferCalculator::GetContract(
@ -93,8 +117,8 @@ REGISTER_CALCULATOR(LappedTensorBufferCalculator);
cc->Inputs().Index(0).Set<tf::Tensor>( cc->Inputs().Index(0).Set<tf::Tensor>(
// tensorflow::Tensor stream. // tensorflow::Tensor stream.
); );
RET_CHECK_EQ(cc->Outputs().NumEntries(), 1) RET_CHECK_LE(cc->Outputs().NumEntries(), 2)
<< "Only one output stream is supported."; << "Only one or two output stream(s) is/are supported.";
if (cc->InputSidePackets().HasTag(kBufferSize)) { if (cc->InputSidePackets().HasTag(kBufferSize)) {
cc->InputSidePackets().Tag(kBufferSize).Set<int>(); cc->InputSidePackets().Tag(kBufferSize).Set<int>();
@ -108,11 +132,15 @@ REGISTER_CALCULATOR(LappedTensorBufferCalculator);
if (cc->InputSidePackets().HasTag(kCalculatorOptions)) { if (cc->InputSidePackets().HasTag(kCalculatorOptions)) {
cc->InputSidePackets() cc->InputSidePackets()
.Tag(kCalculatorOptions) .Tag(kCalculatorOptions)
.Set<LappedTensorBufferCalculatorOptions>(); .Set<LappedTensorBufferCalculator>();
} }
cc->Outputs().Index(0).Set<tf::Tensor>( cc->Outputs().Index(0).Set<tf::Tensor>(
// Output tensorflow::Tensor stream with possibly overlapping steps. // Output tensorflow::Tensor stream with possibly overlapping steps.
); );
// Output timestamp stream with possibly overlapping steps.
if (cc->Outputs().NumEntries() > 1) {
cc->Outputs().Index(1).Set<std::vector<Timestamp>>();
}
return ::mediapipe::OkStatus(); return ::mediapipe::OkStatus();
} }
@ -141,10 +169,13 @@ REGISTER_CALCULATOR(LappedTensorBufferCalculator);
<< "Negative timestamp_offset is not allowed."; << "Negative timestamp_offset is not allowed.";
RET_CHECK_LT(timestamp_offset_, buffer_size_) RET_CHECK_LT(timestamp_offset_, buffer_size_)
<< "output_frame_num_offset has to be less than buffer_size."; << "output_frame_num_offset has to be less than buffer_size.";
RET_CHECK_LT(options_.padding(), buffer_size_)
<< "padding option must be smaller than buffer size.";
timestamp_buffer_ = timestamp_buffer_ =
absl::make_unique<CircularBuffer<Timestamp>>(buffer_size_); absl::make_unique<CircularBuffer<Timestamp>>(buffer_size_);
buffer_ = absl::make_unique<CircularBuffer<tf::Tensor>>(buffer_size_); buffer_ = absl::make_unique<CircularBuffer<tf::Tensor>>(buffer_size_);
steps_until_output_ = buffer_size_; steps_until_output_ = buffer_size_ - options_.padding();
initialized_ = false;
return ::mediapipe::OkStatus(); return ::mediapipe::OkStatus();
} }
@ -156,23 +187,36 @@ REGISTER_CALCULATOR(LappedTensorBufferCalculator);
if (options_.add_batch_dim_to_tensors()) { if (options_.add_batch_dim_to_tensors()) {
RET_CHECK_OK(AddBatchDimension(&input_tensor)); RET_CHECK_OK(AddBatchDimension(&input_tensor));
} }
// Pad frames at the beginning with the first frame.
if (!initialized_) {
for (int i = 0; i < options_.padding(); ++i) {
buffer_->push_back(input_tensor);
timestamp_buffer_->push_back(cc->InputTimestamp());
}
initialized_ = true;
}
buffer_->push_back(input_tensor); buffer_->push_back(input_tensor);
timestamp_buffer_->push_back(cc->InputTimestamp()); timestamp_buffer_->push_back(cc->InputTimestamp());
--steps_until_output_; --steps_until_output_;
if (steps_until_output_ <= 0) { if (steps_until_output_ <= 0) {
auto concatenated = ::absl::make_unique<tf::Tensor>(); MP_RETURN_IF_ERROR(ProcessBuffer(cc));
const tf::Status concat_status = tf::tensor::Concat(
std::vector<tf::Tensor>(buffer_->begin(), buffer_->end()),
concatenated.get());
RET_CHECK(concat_status.ok()) << concat_status.ToString();
cc->Outputs().Index(0).Add(concatenated.release(),
timestamp_buffer_->Get(timestamp_offset_));
steps_until_output_ = buffer_size_ - overlap_;
} }
return ::mediapipe::OkStatus();
}
::mediapipe::Status LappedTensorBufferCalculator::Close(CalculatorContext* cc) {
if (!initialized_ || options_.padding() == 0) {
return ::mediapipe::OkStatus();
}
int last_frame = buffer_size_ - steps_until_output_ - 1;
const auto& pad_frame = buffer_->Get(last_frame);
for (int i = 0; i < steps_until_output_ + options_.padding(); ++i) {
buffer_->push_back(pad_frame);
timestamp_buffer_->push_back(cc->InputTimestamp());
}
MP_RETURN_IF_ERROR(ProcessBuffer(cc));
return ::mediapipe::OkStatus(); return ::mediapipe::OkStatus();
} }
@ -190,4 +234,29 @@ REGISTER_CALCULATOR(LappedTensorBufferCalculator);
return ::mediapipe::OkStatus(); return ::mediapipe::OkStatus();
} }
// Process buffer
::mediapipe::Status LappedTensorBufferCalculator::ProcessBuffer(
CalculatorContext* cc) {
auto concatenated = ::absl::make_unique<tf::Tensor>();
const tf::Status concat_status = tf::tensor::Concat(
std::vector<tf::Tensor>(buffer_->begin(), buffer_->end()),
concatenated.get());
RET_CHECK(concat_status.ok()) << concat_status.ToString();
// Output cancatenated tensor.
cc->Outputs().Index(0).Add(concatenated.release(),
timestamp_buffer_->Get(timestamp_offset_));
if (cc->Outputs().NumEntries() > 1) {
auto output_timestamp = ::absl::make_unique<std::vector<Timestamp>>();
// Output timestamp vector.
*output_timestamp = std::vector<Timestamp>(timestamp_buffer_->begin(),
timestamp_buffer_->end());
RET_CHECK_EQ(output_timestamp->size(), buffer_size_)
<< "Output timestamp size is not correct.";
cc->Outputs().Index(1).Add(output_timestamp.release(),
timestamp_buffer_->Get(timestamp_offset_));
}
steps_until_output_ = buffer_size_ - overlap_;
return ::mediapipe::OkStatus();
}
} // namespace mediapipe } // namespace mediapipe

View File

@ -45,4 +45,8 @@ message LappedTensorBufferCalculatorOptions {
// This is useful for aligning the timestamp to be centered on the input // This is useful for aligning the timestamp to be centered on the input
// range. // range.
optional int32 timestamp_offset = 4 [default = 0]; optional int32 timestamp_offset = 4 [default = 0];
// Amount of padding (repeating of first/last value) to add to the beginning
// and end of the input stream.
optional int32 padding = 5;
} }

View File

@ -31,11 +31,15 @@ namespace tf = ::tensorflow;
class LappedTensorBufferCalculatorTest : public ::testing::Test { class LappedTensorBufferCalculatorTest : public ::testing::Test {
protected: protected:
void SetUpCalculator(int buffer_size, int overlap, bool add_dim, void SetUpCalculator(int buffer_size, int overlap, bool add_dim,
int timestamp_offset) { int timestamp_offset, int padding,
bool timestamp_output) {
CalculatorGraphConfig::Node config; CalculatorGraphConfig::Node config;
config.set_calculator("LappedTensorBufferCalculator"); config.set_calculator("LappedTensorBufferCalculator");
config.add_input_stream("input_tensor"); config.add_input_stream("input_tensor");
config.add_output_stream("output_tensor"); config.add_output_stream("output_tensor");
if (timestamp_output) {
config.add_output_stream("output_timestamp");
}
auto options = config.mutable_options()->MutableExtension( auto options = config.mutable_options()->MutableExtension(
LappedTensorBufferCalculatorOptions::ext); LappedTensorBufferCalculatorOptions::ext);
options->set_buffer_size(buffer_size); options->set_buffer_size(buffer_size);
@ -44,13 +48,14 @@ class LappedTensorBufferCalculatorTest : public ::testing::Test {
options->set_add_batch_dim_to_tensors(true); options->set_add_batch_dim_to_tensors(true);
} }
options->set_timestamp_offset(timestamp_offset); options->set_timestamp_offset(timestamp_offset);
options->set_padding(padding);
runner_ = ::absl::make_unique<CalculatorRunner>(config); runner_ = ::absl::make_unique<CalculatorRunner>(config);
} }
std::unique_ptr<CalculatorRunner> runner_; std::unique_ptr<CalculatorRunner> runner_;
}; };
TEST_F(LappedTensorBufferCalculatorTest, OneToOne) { TEST_F(LappedTensorBufferCalculatorTest, OneToOne) {
SetUpCalculator(1, 0, false, 0); SetUpCalculator(1, 0, false, 0, 0, false);
int num_timesteps = 3; int num_timesteps = 3;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -74,7 +79,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToTwo) {
int buffer_size = 2; int buffer_size = 2;
int overlap = 1; int overlap = 1;
bool add_dim = false; bool add_dim = false;
SetUpCalculator(buffer_size, overlap, add_dim, 0); SetUpCalculator(buffer_size, overlap, add_dim, 0, 0, false);
int num_timesteps = 3; int num_timesteps = 3;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -100,7 +105,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToThree) {
int buffer_size = 3; int buffer_size = 3;
int overlap = 2; int overlap = 2;
bool add_dim = false; bool add_dim = false;
SetUpCalculator(buffer_size, overlap, add_dim, 0); SetUpCalculator(buffer_size, overlap, add_dim, 0, 0, false);
int num_timesteps = 3; int num_timesteps = 3;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -126,7 +131,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToThreeSkip) {
int buffer_size = 3; int buffer_size = 3;
int overlap = 1; int overlap = 1;
bool add_dim = false; bool add_dim = false;
SetUpCalculator(buffer_size, overlap, add_dim, 0); SetUpCalculator(buffer_size, overlap, add_dim, 0, 0, false);
int num_timesteps = 3; int num_timesteps = 3;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -152,7 +157,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToThreeBatch) {
int buffer_size = 3; int buffer_size = 3;
int overlap = 2; int overlap = 2;
bool add_dim = true; bool add_dim = true;
SetUpCalculator(buffer_size, overlap, add_dim, 0); SetUpCalculator(buffer_size, overlap, add_dim, 0, 0, false);
int num_timesteps = 3; int num_timesteps = 3;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -180,7 +185,7 @@ TEST_F(LappedTensorBufferCalculatorTest, NegativeTimestampOffsetFails) {
int overlap = 15; int overlap = 15;
bool add_dim = true; bool add_dim = true;
int timestamp_offset = -7; int timestamp_offset = -7;
SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset); SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset, 0, false);
int num_timesteps = 20; int num_timesteps = 20;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -197,7 +202,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OutOfRangeTimestampOffsetFails) {
int overlap = 15; int overlap = 15;
bool add_dim = true; bool add_dim = true;
int timestamp_offset = buffer_size; int timestamp_offset = buffer_size;
SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset); SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset, 0, false);
int num_timesteps = 20; int num_timesteps = 20;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -214,7 +219,7 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToThreeBatchTimestampOffset) {
int overlap = 15; int overlap = 15;
bool add_dim = true; bool add_dim = true;
int timestamp_offset = 7; int timestamp_offset = 7;
SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset); SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset, 0, false);
int num_timesteps = 20; int num_timesteps = 20;
for (int i = 0; i < num_timesteps; ++i) { for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>( auto input = ::absl::make_unique<tensorflow::Tensor>(
@ -236,5 +241,37 @@ TEST_F(LappedTensorBufferCalculatorTest, OneToThreeBatchTimestampOffset) {
} }
} }
TEST_F(LappedTensorBufferCalculatorTest,
OneToThreeBatchTimestampOffsetPadding) {
int buffer_size = 12;
int overlap = 6;
bool add_dim = true;
int timestamp_offset = 3;
int padding = 0;
SetUpCalculator(buffer_size, overlap, add_dim, timestamp_offset, padding,
true);
int num_timesteps = 20;
for (int i = 0; i < num_timesteps; ++i) {
auto input = ::absl::make_unique<tensorflow::Tensor>(
tensorflow::DT_FLOAT, tensorflow::TensorShape({1}));
input->tensor<float, 1>()(0) = i;
runner_->MutableInputs()->Index(0).packets.push_back(
Adopt(input.release()).At(Timestamp(i)));
}
ASSERT_TRUE(runner_->Run().ok());
const int output_size = num_timesteps / buffer_size + 1;
const std::vector<Packet>& output_packets =
runner_->Outputs().Index(0).packets;
ASSERT_EQ(output_size, output_packets.size());
for (int i = 0; i < output_size; ++i) {
int64 value = output_packets[i].Timestamp().Value();
ASSERT_EQ(i * overlap + timestamp_offset, value);
}
const std::vector<Packet>& output_timestamps =
runner_->Outputs().Index(1).packets;
ASSERT_EQ(output_size, output_timestamps.size());
}
} // namespace } // namespace
} // namespace mediapipe } // namespace mediapipe