Project import generated by Copybara.

PiperOrigin-RevId: 253489161
This commit is contained in:
MediaPipe Team
2019-06-16 16:06:57 -07:00
committed by jqtang
commit d68f5e4169
844 changed files with 134997 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
# Copyright 2019 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.
#
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//visibility:private"])
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
proto_library(
name = "flow_to_image_calculator_proto",
srcs = ["flow_to_image_calculator.proto"],
visibility = ["//visibility:public"],
deps = ["//mediapipe/framework:calculator_proto"],
)
proto_library(
name = "opencv_video_encoder_calculator_proto",
srcs = ["opencv_video_encoder_calculator.proto"],
visibility = ["//visibility:public"],
deps = ["//mediapipe/framework:calculator_proto"],
)
mediapipe_cc_proto_library(
name = "flow_to_image_calculator_cc_proto",
srcs = ["flow_to_image_calculator.proto"],
cc_deps = ["//mediapipe/framework:calculator_cc_proto"],
visibility = ["//mediapipe:__subpackages__"],
deps = [":flow_to_image_calculator_proto"],
)
mediapipe_cc_proto_library(
name = "opencv_video_encoder_calculator_cc_proto",
srcs = ["opencv_video_encoder_calculator.proto"],
cc_deps = ["//mediapipe/framework:calculator_cc_proto"],
visibility = ["//mediapipe:__subpackages__"],
deps = [":opencv_video_encoder_calculator_proto"],
)
cc_library(
name = "flow_to_image_calculator",
srcs = ["flow_to_image_calculator.cc"],
visibility = [
"//visibility:public",
],
deps = [
"//mediapipe/calculators/video:flow_to_image_calculator_cc_proto",
"//mediapipe/calculators/video/tool:flow_quantizer_model",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:image_format_cc_proto",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/formats/motion:optical_flow_field",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:parse_text_proto",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
alwayslink = 1,
)
cc_library(
name = "opencv_video_decoder_calculator",
srcs = ["opencv_video_decoder_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:image_format_cc_proto",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/formats:video_stream_header",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:opencv_video",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/framework/tool:status_util",
],
alwayslink = 1,
)
cc_library(
name = "opencv_video_encoder_calculator",
srcs = ["opencv_video_encoder_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":opencv_video_encoder_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/formats:video_stream_header",
"//mediapipe/framework/port:file_helpers",
"//mediapipe/framework/port:opencv_highgui",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:opencv_video",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:source_location",
"//mediapipe/framework/port:status",
"//mediapipe/framework/tool:status_util",
"@com_google_absl//absl/strings",
],
alwayslink = 1,
)
cc_test(
name = "opencv_video_decoder_calculator_test",
srcs = ["opencv_video_decoder_calculator_test.cc"],
data = ["//mediapipe/calculators/video/testdata:test_videos"],
deps = [
":opencv_video_decoder_calculator",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework/deps:file_path",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/formats:video_stream_header",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:integral_types",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:parse_text_proto",
],
)
cc_test(
name = "opencv_video_encoder_calculator_test",
srcs = ["opencv_video_encoder_calculator_test.cc"],
data = ["//mediapipe/calculators/video/testdata:test_videos"],
deps = [
":opencv_video_decoder_calculator",
":opencv_video_encoder_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:packet",
"//mediapipe/framework/deps:file_path",
"//mediapipe/framework/formats:deleting_file",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/formats:video_stream_header",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:integral_types",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:opencv_highgui",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:opencv_video",
"//mediapipe/framework/port:parse_text_proto",
],
)
@@ -0,0 +1,114 @@
// Copyright 2019 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.
// MediaPipe calculator to take a flow field as input, and outputs a normalized
// RGB image where the B channel is forced to zero.
// TODO: Add video stream header for visualization
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "mediapipe/calculators/video/flow_to_image_calculator.pb.h"
#include "mediapipe/calculators/video/tool/flow_quantizer_model.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/image_format.pb.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/motion/optical_flow_field.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/framework/port/parse_text_proto.h"
namespace mediapipe {
// Reads optical flow fields defined in
// mediapipe/framework/formats/motion/optical_flow_field.h,
// returns a VideoFrame with 2 channels (v_x and v_y), each channel is quantized
// to 0-255.
//
// Example config:
// node {
// calculator: "FlowToImageCalculator"
// input_stream: "flow_fields"
// output_stream: "frames"
// options: {
// [type.googleapis.com/mediapipe.FlowToImageCalculatorOptions]:{
// min_value: -40.0
// max_value: 40.0
// }
// }
// }
class FlowToImageCalculator : public CalculatorBase {
public:
FlowToImageCalculator() {}
~FlowToImageCalculator() override {}
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
private:
FlowQuantizerModel model_;
};
::mediapipe::Status FlowToImageCalculator::GetContract(CalculatorContract* cc) {
cc->Inputs().Index(0).Set<OpticalFlowField>();
cc->Outputs().Index(0).Set<ImageFrame>();
// Model sanity check
const auto& options = cc->Options<FlowToImageCalculatorOptions>();
if (options.min_value() >= options.max_value()) {
return ::mediapipe::InvalidArgumentError("Invalid quantizer model.");
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FlowToImageCalculator::Open(CalculatorContext* cc) {
const auto& options = cc->Options<FlowToImageCalculatorOptions>();
// Fill the the model_data, ideally we want to train the model, but we omit
// the step for now, and takes the (min, max) range from protobuf.
const QuantizerModelData& model_data =
ParseTextProtoOrDie<QuantizerModelData>(
absl::StrFormat("min_value:%f min_value:%f max_value:%f max_value:%f",
options.min_value(), options.min_value(),
options.max_value(), options.max_value()));
model_.LoadFromProto(model_data);
return ::mediapipe::OkStatus();
}
::mediapipe::Status FlowToImageCalculator::Process(CalculatorContext* cc) {
const auto& input = cc->Inputs().Index(0).Get<OpticalFlowField>();
// Input flow is 2-channel with x-dim flow and y-dim flow.
// Convert it to a ImageFrame in SRGB space, the 3rd channel is not used (0).
const cv::Mat_<cv::Point2f>& flow = input.flow_data();
std::unique_ptr<ImageFrame> output(
new ImageFrame(ImageFormat::SRGB, input.width(), input.height()));
cv::Mat image = ::mediapipe::formats::MatView(output.get());
for (int j = 0; j != input.height(); ++j) {
for (int i = 0; i != input.width(); ++i) {
image.at<cv::Vec3b>(j, i) =
cv::Vec3b(model_.Apply(flow.at<cv::Point2f>(j, i).x, 0),
model_.Apply(flow.at<cv::Point2f>(j, i).y, 1), 0);
}
}
cc->Outputs().Index(0).Add(output.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
}
REGISTER_CALCULATOR(FlowToImageCalculator);
} // namespace mediapipe
@@ -0,0 +1,29 @@
// Copyright 2019 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";
// Specifies the maximum and minimum value to truncate when normalize optical
// flow fields.
message FlowToImageCalculatorOptions {
extend CalculatorOptions {
optional FlowToImageCalculatorOptions ext = 69508592;
}
optional float min_value = 1 [default = -40.0];
optional float max_value = 2 [default = 40.0];
}
@@ -0,0 +1,184 @@
// Copyright 2019 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 "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/image_format.pb.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/video_stream_header.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/opencv_video_inc.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/tool/status_util.h"
namespace mediapipe {
namespace {
// cv::VideoCapture set data type to unsigned char by default. Therefore, the
// image format is only related to the number of channles the cv::Mat has.
ImageFormat::Format GetImageFormat(int num_channels) {
ImageFormat::Format format;
switch (num_channels) {
case 1:
format = ImageFormat::GRAY8;
break;
case 3:
format = ImageFormat::SRGB;
break;
case 4:
format = ImageFormat::SRGBA;
break;
default:
format = ImageFormat::UNKNOWN;
break;
}
return format;
}
} // namespace
// This Calculator takes no input streams and produces video packets.
// All streams and input side packets are specified using tags and all of them
// are optional.
//
// Output Streams:
// VIDEO: Output video frames (ImageFrame).
// VIDEO_PRESTREAM:
// Optional video header information output at
// Timestamp::PreStream() for the corresponding stream.
// Input Side Packets:
// INPUT_FILE_PATH: The input file path.
//
// Example config:
// node {
// calculator: "OpenCvVideoDecoderCalculator"
// input_side_packet: "INPUT_FILE_PATH:input_file_path"
// output_stream: "VIDEO:video_frames"
// output_stream: "VIDEO_PRESTREAM:video_header"
// }
class OpenCvVideoDecoderCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
cc->InputSidePackets().Tag("INPUT_FILE_PATH").Set<std::string>();
cc->Outputs().Tag("VIDEO").Set<ImageFrame>();
if (cc->Outputs().HasTag("VIDEO_PRESTREAM")) {
cc->Outputs().Tag("VIDEO_PRESTREAM").Set<VideoHeader>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) override {
const std::string& input_file_path =
cc->InputSidePackets().Tag("INPUT_FILE_PATH").Get<std::string>();
cap_ = absl::make_unique<cv::VideoCapture>(input_file_path);
if (!cap_->isOpened()) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Fail to open video file at " << input_file_path;
}
width_ = static_cast<int>(cap_->get(cv::CAP_PROP_FRAME_WIDTH));
height_ = static_cast<int>(cap_->get(cv::CAP_PROP_FRAME_HEIGHT));
double fps = static_cast<double>(cap_->get(cv::CAP_PROP_FPS));
frame_count_ = static_cast<int>(cap_->get(cv::CAP_PROP_FRAME_COUNT));
// Unfortunately, cap_->get(cv::CAP_PROP_FORMAT) always returns CV_8UC1
// back. To get correct image format, we read the first frame from the video
// and get the number of channels.
cv::Mat frame;
cap_->read(frame);
if (frame.empty()) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Fail to read any frames from the video file at "
<< input_file_path;
}
format_ = GetImageFormat(frame.channels());
if (format_ == ImageFormat::UNKNOWN) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Unsupported video format of the video file at "
<< input_file_path;
}
if (fps <= 0 || frame_count_ <= 0 || width_ <= 0 || height_ <= 0) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Fail to make video header due to the incorrect metadata from "
"the video file at "
<< input_file_path;
}
auto header = absl::make_unique<VideoHeader>();
header->format = format_;
header->width = width_;
header->height = height_;
header->frame_rate = fps;
header->duration = frame_count_ / fps;
if (cc->Outputs().HasTag("VIDEO_PRESTREAM")) {
cc->Outputs()
.Tag("VIDEO_PRESTREAM")
.Add(header.release(), Timestamp::PreStream());
}
// Rewind to the very first frame.
cap_->set(cv::CAP_PROP_POS_AVI_RATIO, 0);
return ::mediapipe::OkStatus();
}
::mediapipe::Status Process(CalculatorContext* cc) override {
auto image_frame = absl::make_unique<ImageFrame>(format_, width_, height_,
/*alignment_boundary=*/1);
// Use microsecond as the unit of time.
Timestamp timestamp(cap_->get(cv::CAP_PROP_POS_MSEC) * 1000);
if (format_ == ImageFormat::GRAY8) {
cv::Mat frame = formats::MatView(image_frame.get());
cap_->read(frame);
if (frame.empty()) {
return tool::StatusStop();
}
} else {
cv::Mat tmp_frame;
cap_->read(tmp_frame);
if (tmp_frame.empty()) {
return tool::StatusStop();
}
if (format_ == ImageFormat::SRGB) {
cv::cvtColor(tmp_frame, formats::MatView(image_frame.get()),
cv::COLOR_BGR2RGB);
} else if (format_ == ImageFormat::SRGBA) {
cv::cvtColor(tmp_frame, formats::MatView(image_frame.get()),
cv::COLOR_BGRA2RGBA);
}
}
cc->Outputs().Tag("VIDEO").Add(image_frame.release(), timestamp);
decoded_frames_++;
return ::mediapipe::OkStatus();
}
::mediapipe::Status Close(CalculatorContext* cc) override {
if (cap_ && cap_->isOpened()) {
cap_->release();
}
if (decoded_frames_ != frame_count_) {
LOG(WARNING) << "Not all the frames are decoded (total frames: "
<< frame_count_ << " vs decoded frames: " << decoded_frames_
<< ").";
}
return ::mediapipe::OkStatus();
}
private:
std::unique_ptr<cv::VideoCapture> cap_;
int width_;
int height_;
int frame_count_;
int decoded_frames_ = 0;
ImageFormat::Format format_;
};
REGISTER_CALCULATOR(OpenCvVideoDecoderCalculator);
} // namespace mediapipe
@@ -0,0 +1,161 @@
// Copyright 2019 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 "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/deps/file_path.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/video_stream_header.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
namespace {
TEST(OpenCvVideoDecoderCalculatorTest, TestMp4Avc720pVideo) {
CalculatorGraphConfig::Node node_config =
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream")");
CalculatorRunner runner(node_config);
runner.MutableSidePackets()->Tag("INPUT_FILE_PATH") = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_MP4_AVC720P_AAC.video"));
MEDIAPIPE_EXPECT_OK(runner.Run());
EXPECT_EQ(runner.Outputs().Tag("VIDEO_PRESTREAM").packets.size(), 1);
MEDIAPIPE_EXPECT_OK(runner.Outputs()
.Tag("VIDEO_PRESTREAM")
.packets[0]
.ValidateAsType<VideoHeader>());
const mediapipe::VideoHeader& header =
runner.Outputs().Tag("VIDEO_PRESTREAM").packets[0].Get<VideoHeader>();
EXPECT_EQ(ImageFormat::SRGB, header.format);
EXPECT_EQ(1280, header.width);
EXPECT_EQ(640, header.height);
EXPECT_FLOAT_EQ(6.0f, header.duration);
EXPECT_FLOAT_EQ(30.0f, header.frame_rate);
EXPECT_EQ(180, runner.Outputs().Tag("VIDEO").packets.size());
for (int i = 0; i < 180; ++i) {
Packet image_frame_packet = runner.Outputs().Tag("VIDEO").packets[i];
cv::Mat output_mat =
formats::MatView(&(image_frame_packet.Get<ImageFrame>()));
EXPECT_EQ(1280, output_mat.size().width);
EXPECT_EQ(640, output_mat.size().height);
EXPECT_EQ(3, output_mat.channels());
cv::Scalar s = cv::mean(output_mat);
for (int i = 0; i < 3; ++i) {
EXPECT_GT(s[i], 0);
EXPECT_LT(s[i], 255);
}
}
}
TEST(OpenCvVideoDecoderCalculatorTest, TestFlvH264Video) {
CalculatorGraphConfig::Node node_config =
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream")");
CalculatorRunner runner(node_config);
runner.MutableSidePackets()->Tag("INPUT_FILE_PATH") = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_FLV_H264_AAC.video"));
MEDIAPIPE_EXPECT_OK(runner.Run());
EXPECT_EQ(runner.Outputs().Tag("VIDEO_PRESTREAM").packets.size(), 1);
MEDIAPIPE_EXPECT_OK(runner.Outputs()
.Tag("VIDEO_PRESTREAM")
.packets[0]
.ValidateAsType<VideoHeader>());
const mediapipe::VideoHeader& header =
runner.Outputs().Tag("VIDEO_PRESTREAM").packets[0].Get<VideoHeader>();
EXPECT_EQ(ImageFormat::SRGB, header.format);
EXPECT_EQ(640, header.width);
EXPECT_EQ(320, header.height);
// TODO: The actual header.duration is 6.0666666f and the frame_rate
// can be either 30.30303f (with opencv2) or 30f (with opencv3 and opencv4).
// EXPECT_FLOAT_EQ(6.0f, header.duration);
// EXPECT_FLOAT_EQ(30.0f, header.frame_rate);
EXPECT_EQ(180, runner.Outputs().Tag("VIDEO").packets.size());
for (int i = 0; i < 180; ++i) {
Packet image_frame_packet = runner.Outputs().Tag("VIDEO").packets[i];
cv::Mat output_mat =
formats::MatView(&(image_frame_packet.Get<ImageFrame>()));
EXPECT_EQ(640, output_mat.size().width);
EXPECT_EQ(320, output_mat.size().height);
EXPECT_EQ(3, output_mat.channels());
cv::Scalar s = cv::mean(output_mat);
for (int i = 0; i < 3; ++i) {
EXPECT_GT(s[i], 0);
EXPECT_LT(s[i], 255);
}
}
}
TEST(OpenCvVideoDecoderCalculatorTest, TestMkvVp8Video) {
CalculatorGraphConfig::Node node_config =
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream")");
CalculatorRunner runner(node_config);
runner.MutableSidePackets()->Tag("INPUT_FILE_PATH") = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_MKV_VP8_VORBIS.video"));
MEDIAPIPE_EXPECT_OK(runner.Run());
EXPECT_EQ(runner.Outputs().Tag("VIDEO_PRESTREAM").packets.size(), 1);
MEDIAPIPE_EXPECT_OK(runner.Outputs()
.Tag("VIDEO_PRESTREAM")
.packets[0]
.ValidateAsType<VideoHeader>());
const mediapipe::VideoHeader& header =
runner.Outputs().Tag("VIDEO_PRESTREAM").packets[0].Get<VideoHeader>();
EXPECT_EQ(ImageFormat::SRGB, header.format);
EXPECT_EQ(640, header.width);
EXPECT_EQ(320, header.height);
EXPECT_FLOAT_EQ(6.0f, header.duration);
EXPECT_FLOAT_EQ(30.0f, header.frame_rate);
EXPECT_EQ(180, runner.Outputs().Tag("VIDEO").packets.size());
for (int i = 0; i < 180; ++i) {
Packet image_frame_packet = runner.Outputs().Tag("VIDEO").packets[i];
cv::Mat output_mat =
formats::MatView(&(image_frame_packet.Get<ImageFrame>()));
EXPECT_EQ(640, output_mat.size().width);
EXPECT_EQ(320, output_mat.size().height);
EXPECT_EQ(3, output_mat.channels());
cv::Scalar s = cv::mean(output_mat);
for (int i = 0; i < 3; ++i) {
EXPECT_GT(s[i], 0);
EXPECT_LT(s[i], 255);
}
}
}
} // namespace
} // namespace mediapipe
@@ -0,0 +1,176 @@
// Copyright 2019 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 <memory>
#include <string>
#include <vector>
#include "absl/strings/str_split.h"
#include "mediapipe/calculators/video/opencv_video_encoder_calculator.pb.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/video_stream_header.h"
#include "mediapipe/framework/port/file_helpers.h"
#include "mediapipe/framework/port/opencv_highgui_inc.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/opencv_video_inc.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/source_location.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/port/status_builder.h"
#include "mediapipe/framework/tool/status_util.h"
namespace mediapipe {
// Encodes the input video stream and produces a media file.
// The media file can be output to the output_file_path specified as a side
// packet. Currently, the calculator only supports one video stream (in
// mediapipe::ImageFrame).
//
// Example config to generate the output video file:
//
// node {
// calculator: "OpenCvVideoEncoderCalculator"
// input_stream: "VIDEO:video"
// input_stream: "VIDEO_PRESTREAM:video_header"
// input_side_packet: "OUTPUT_FILE_PATH:output_file_path"
// node_options {
// [type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
// codec: "avc1"
// video_format: "mp4"
// }
// }
// }
class OpenCvVideoEncoderCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
private:
::mediapipe::Status SetUpVideoWriter(float frame_rate, int width, int height);
std::string output_file_path_;
int four_cc_;
std::unique_ptr<cv::VideoWriter> writer_;
};
::mediapipe::Status OpenCvVideoEncoderCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag("VIDEO"));
cc->Inputs().Tag("VIDEO").Set<ImageFrame>();
if (cc->Inputs().HasTag("VIDEO_PRESTREAM")) {
cc->Inputs().Tag("VIDEO_PRESTREAM").Set<VideoHeader>();
}
RET_CHECK(cc->InputSidePackets().HasTag("OUTPUT_FILE_PATH"));
cc->InputSidePackets().Tag("OUTPUT_FILE_PATH").Set<std::string>();
return ::mediapipe::OkStatus();
}
::mediapipe::Status OpenCvVideoEncoderCalculator::Open(CalculatorContext* cc) {
OpenCvVideoEncoderCalculatorOptions options =
cc->Options<OpenCvVideoEncoderCalculatorOptions>();
RET_CHECK(options.has_codec() && options.codec().length() == 4)
<< "A 4-character codec code must be specified in "
"OpenCvVideoEncoderCalculatorOptions";
const char* codec_array = options.codec().c_str();
four_cc_ = mediapipe::fourcc(codec_array[0], codec_array[1], codec_array[2],
codec_array[3]);
RET_CHECK(!options.video_format().empty())
<< "Video format must be specified in "
"OpenCvVideoEncoderCalculatorOptions";
output_file_path_ =
cc->InputSidePackets().Tag("OUTPUT_FILE_PATH").Get<std::string>();
std::vector<std::string> splited_file_path =
absl::StrSplit(output_file_path_, '.');
RET_CHECK(splited_file_path.size() >= 2 &&
splited_file_path[splited_file_path.size() - 1] ==
options.video_format())
<< "The output file path is invalid.";
// If the video header will be available, the video metadata will be fetched
// from the video header directly. The calculator will receive the video
// header packet at timestamp prestream.
if (cc->Inputs().HasTag("VIDEO_PRESTREAM")) {
return ::mediapipe::OkStatus();
}
return SetUpVideoWriter(options.fps(), options.width(), options.height());
}
::mediapipe::Status OpenCvVideoEncoderCalculator::Process(
CalculatorContext* cc) {
if (cc->InputTimestamp() == Timestamp::PreStream()) {
const VideoHeader& video_header =
cc->Inputs().Tag("VIDEO_PRESTREAM").Get<VideoHeader>();
return SetUpVideoWriter(video_header.frame_rate, video_header.width,
video_header.height);
}
const ImageFrame& image_frame =
cc->Inputs().Tag("VIDEO").Value().Get<ImageFrame>();
ImageFormat::Format format = image_frame.Format();
cv::Mat frame;
if (format == ImageFormat::GRAY8) {
frame = formats::MatView(&image_frame);
if (frame.empty()) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Receive empty frame at timestamp "
<< cc->Inputs().Tag("VIDEO").Value().Timestamp()
<< " in OpenCvVideoEncoderCalculator::Process()";
}
} else {
cv::Mat tmp_frame = formats::MatView(&image_frame);
if (tmp_frame.empty()) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Receive empty frame at timestamp "
<< cc->Inputs().Tag("VIDEO").Value().Timestamp()
<< " in OpenCvVideoEncoderCalculator::Process()";
}
if (format == ImageFormat::SRGB) {
cv::cvtColor(tmp_frame, frame, cv::COLOR_BGR2RGB);
} else if (format == ImageFormat::SRGBA) {
cv::cvtColor(tmp_frame, frame, cv::COLOR_BGRA2RGBA);
} else {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Unsupported image format: " << format;
}
}
writer_->write(frame);
return ::mediapipe::OkStatus();
}
::mediapipe::Status OpenCvVideoEncoderCalculator::Close(CalculatorContext* cc) {
if (writer_ && writer_->isOpened()) {
writer_->release();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status OpenCvVideoEncoderCalculator::SetUpVideoWriter(
float frame_rate, int width, int height) {
RET_CHECK(frame_rate > 0 && width > 0 && height > 0)
<< "Invalid video metadata: frame_rate=" << frame_rate
<< ", width=" << width << ", height=" << height;
writer_ = absl::make_unique<cv::VideoWriter>(
output_file_path_, four_cc_, frame_rate, cv::Size(width, height));
if (!writer_->isOpened()) {
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
<< "Fail to open file at " << output_file_path_;
}
return ::mediapipe::OkStatus();
}
REGISTER_CALCULATOR(OpenCvVideoEncoderCalculator);
} // namespace mediapipe
@@ -0,0 +1,37 @@
// Copyright 2019 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 OpenCvVideoEncoderCalculatorOptions {
extend CalculatorOptions {
optional OpenCvVideoEncoderCalculatorOptions ext = 207936763;
}
// The 4-character code of the codec to encode the video.
optional string codec = 1;
// The video format of the output video file.
optional string video_format = 2;
// The frame rate in Hz at which the video frames are output.
optional double fps = 3;
// Dimensions of the video in pixels.
optional int32 width = 4;
optional int32 height = 5;
}
@@ -0,0 +1,218 @@
// Copyright 2019 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 "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/deps/file_path.h"
#include "mediapipe/framework/formats/deleting_file.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/video_stream_header.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/opencv_highgui_inc.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/opencv_video_inc.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
namespace {
// Temporarily disable the test.
// TODO: Investigate the “Could not open codec 'libx264'” error with
// opencv2.
TEST(OpenCvVideoEncoderCalculatorTest, DISABLED_TestMp4Avc720pVideo) {
CalculatorGraphConfig config = ParseTextProtoOrDie<CalculatorGraphConfig>(R"(
node {
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream"
}
node {
calculator: "OpenCvVideoEncoderCalculator"
input_stream: "VIDEO:video"
input_stream: "VIDEO_PRESTREAM:video_prestream"
input_side_packet: "OUTPUT_FILE_PATH:output_file_path"
node_options {
[type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
codec: "avc1"
video_format: "mp4"
}
}
}
)");
std::map<std::string, Packet> input_side_packets;
input_side_packets["input_file_path"] = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_MP4_AVC720P_AAC.video"));
const std::string output_file_path = "/tmp/tmp_video.mp4";
DeletingFile deleting_file(output_file_path, true);
input_side_packets["output_file_path"] =
MakePacket<std::string>(output_file_path);
CalculatorGraph graph;
MEDIAPIPE_ASSERT_OK(graph.Initialize(config, input_side_packets));
StatusOrPoller status_or_poller =
graph.AddOutputStreamPoller("video_prestream");
ASSERT_TRUE(status_or_poller.ok());
OutputStreamPoller poller = std::move(status_or_poller.ValueOrDie());
MEDIAPIPE_ASSERT_OK(graph.StartRun({}));
Packet packet;
while (poller.Next(&packet)) {
}
MEDIAPIPE_ASSERT_OK(graph.WaitUntilDone());
const VideoHeader& video_header = packet.Get<VideoHeader>();
// Checks the generated video file has the same width, height, fps, and
// duration as the original one.
cv::VideoCapture cap(output_file_path);
ASSERT_TRUE(cap.isOpened());
EXPECT_EQ(video_header.width,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH)));
EXPECT_EQ(video_header.height,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT)));
EXPECT_EQ(video_header.frame_rate,
static_cast<double>(cap.get(cv::CAP_PROP_FPS)));
EXPECT_EQ(video_header.duration,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_COUNT) /
cap.get(cv::CAP_PROP_FPS)));
}
TEST(OpenCvVideoEncoderCalculatorTest, TestFlvH264Video) {
CalculatorGraphConfig config = ParseTextProtoOrDie<CalculatorGraphConfig>(R"(
node {
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream"
}
node {
calculator: "OpenCvVideoEncoderCalculator"
input_stream: "VIDEO:video"
input_stream: "VIDEO_PRESTREAM:video_prestream"
input_side_packet: "OUTPUT_FILE_PATH:output_file_path"
node_options {
[type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
codec: "MJPG"
video_format: "avi"
}
}
}
)");
std::map<std::string, Packet> input_side_packets;
input_side_packets["input_file_path"] = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_FLV_H264_AAC.video"));
const std::string output_file_path = "/tmp/tmp_video.avi";
DeletingFile deleting_file(output_file_path, true);
input_side_packets["output_file_path"] =
MakePacket<std::string>(output_file_path);
CalculatorGraph graph;
MEDIAPIPE_ASSERT_OK(graph.Initialize(config, input_side_packets));
StatusOrPoller status_or_poller =
graph.AddOutputStreamPoller("video_prestream");
ASSERT_TRUE(status_or_poller.ok());
OutputStreamPoller poller = std::move(status_or_poller.ValueOrDie());
MEDIAPIPE_ASSERT_OK(graph.StartRun({}));
Packet packet;
while (poller.Next(&packet)) {
}
MEDIAPIPE_ASSERT_OK(graph.WaitUntilDone());
const VideoHeader& video_header = packet.Get<VideoHeader>();
// Checks the generated video file has the same width, height, fps, and
// duration as the original one.
cv::VideoCapture cap(output_file_path);
ASSERT_TRUE(cap.isOpened());
EXPECT_EQ(video_header.width,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH)));
EXPECT_EQ(video_header.height,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT)));
// TODO: The actual header.duration is 6.0666666f and the frame_rate
// can be either 30.30303f (with opencv2) or 30f (with opencv3 and opencv4).
// EXPECT_EQ(video_header.frame_rate,
// static_cast<double>(cap.get(cv::CAP_PROP_FPS)));
// EXPECT_EQ(video_header.duration,
// static_cast<int>(cap.get(cv::CAP_PROP_FRAME_COUNT) /
// cap.get(cv::CAP_PROP_FPS)));
}
TEST(OpenCvVideoEncoderCalculatorTest, TestMkvVp8Video) {
CalculatorGraphConfig config = ParseTextProtoOrDie<CalculatorGraphConfig>(R"(
node {
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_file_path"
output_stream: "VIDEO:video"
output_stream: "VIDEO_PRESTREAM:video_prestream"
}
node {
calculator: "OpenCvVideoEncoderCalculator"
input_stream: "VIDEO:video"
input_stream: "VIDEO_PRESTREAM:video_prestream"
input_side_packet: "OUTPUT_FILE_PATH:output_file_path"
node_options {
[type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
codec: "PIM1"
video_format: "mkv"
}
}
}
)");
std::map<std::string, Packet> input_side_packets;
input_side_packets["input_file_path"] = MakePacket<std::string>(
file::JoinPath("./",
"/mediapipe/calculators/video/"
"testdata/format_MKV_VP8_VORBIS.video"));
const std::string output_file_path = "/tmp/tmp_video.mkv";
DeletingFile deleting_file(output_file_path, true);
input_side_packets["output_file_path"] =
MakePacket<std::string>(output_file_path);
CalculatorGraph graph;
MEDIAPIPE_ASSERT_OK(graph.Initialize(config, input_side_packets));
StatusOrPoller status_or_poller =
graph.AddOutputStreamPoller("video_prestream");
ASSERT_TRUE(status_or_poller.ok());
OutputStreamPoller poller = std::move(status_or_poller.ValueOrDie());
MEDIAPIPE_ASSERT_OK(graph.StartRun({}));
Packet packet;
while (poller.Next(&packet)) {
}
MEDIAPIPE_ASSERT_OK(graph.WaitUntilDone());
const VideoHeader& video_header = packet.Get<VideoHeader>();
// Checks the generated video file has the same width, height, fps, and
// duration as the original one.
cv::VideoCapture cap(output_file_path);
ASSERT_TRUE(cap.isOpened());
EXPECT_EQ(video_header.width,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_WIDTH)));
EXPECT_EQ(video_header.height,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_HEIGHT)));
EXPECT_EQ(video_header.frame_rate,
static_cast<double>(cap.get(cv::CAP_PROP_FPS)));
EXPECT_EQ(video_header.duration,
static_cast<int>(cap.get(cv::CAP_PROP_FRAME_COUNT) /
cap.get(cv::CAP_PROP_FPS)));
}
} // namespace
} // namespace mediapipe
+26
View File
@@ -0,0 +1,26 @@
# Copyright 2019 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.
#
licenses(["notice"]) # Apache 2.0
filegroup(
name = "test_videos",
srcs = [
"format_FLV_H264_AAC.video",
"format_MKV_VP8_VORBIS.video",
"format_MP4_AVC720P_AAC.video",
],
visibility = ["//visibility:public"],
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
+50
View File
@@ -0,0 +1,50 @@
#
# Copyright 2019 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.
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/calculators/video:__subpackages__"])
exports_files(["LICENSE"])
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
proto_library(
name = "flow_quantizer_model_proto",
srcs = ["flow_quantizer_model.proto"],
)
mediapipe_cc_proto_library(
name = "flow_quantizer_model_cc_proto",
srcs = ["flow_quantizer_model.proto"],
visibility = ["//mediapipe:__subpackages__"],
deps = [":flow_quantizer_model_proto"],
)
cc_library(
name = "flow_quantizer_model",
srcs = ["flow_quantizer_model.cc"],
hdrs = ["flow_quantizer_model.h"],
deps = [
"//mediapipe/calculators/video/tool:flow_quantizer_model_cc_proto",
"//mediapipe/framework:type_map",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats/motion:optical_flow_field",
"//mediapipe/framework/port:integral_types",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/tool:status_util",
],
alwayslink = 1,
)
@@ -0,0 +1,78 @@
// Copyright 2019 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 "mediapipe/calculators/video/tool/flow_quantizer_model.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/type_map.h"
namespace mediapipe {
// Uniform normalization to 0-255.
uint8 FlowQuantizerModel::Apply(const float val, const int channel) const {
CHECK_LT(channel, model_.min_value_size());
const auto& min_value = model_.min_value(channel);
const auto& max_value = model_.max_value(channel);
QCHECK_GT(max_value, min_value);
float res = (val - min_value) / (max_value - min_value);
if (res < 0.0) {
res = 0.0;
} else if (res > 1.0) {
res = 1.0;
}
return static_cast<uint8>(res * 255);
}
void FlowQuantizerModel::LoadFromProto(const QuantizerModelData& data) {
QCHECK_GT(data.max_value(0), data.min_value(0));
QCHECK_GT(data.max_value(1), data.min_value(1));
model_ = data;
}
const QuantizerModelData& FlowQuantizerModel::GetModelData() const {
return model_;
}
// Used for training, update the (min, max) range. We want to estimate the range
// of optical flow fields (Theorectically it is (-num_pixels_along_diag,
// num_pixels_along_diag).
// TODO: Taking the min and max over all training flow fields might be
// sensitive to noise. We should use more robust statistics.
void FlowQuantizerModel::AddSampleFlowField(const OpticalFlowField& flow) {
CHECK_EQ(model_.min_value_size(), 2);
const cv::Mat_<cv::Point2f>& flow_mat = flow.flow_data();
for (int i = 0; i != flow.width(); ++i) {
for (int j = 0; j != flow.height(); ++j) {
const auto& x = flow_mat.at<cv::Point2f>(i, j).x;
const auto& y = flow_mat.at<cv::Point2f>(i, j).y;
// Always use the minimum and maximum value occurred in training flow
// fields.
model_.set_min_value(0, std::min<float>(x, model_.min_value(0)));
model_.set_min_value(1, std::min<float>(y, model_.min_value(1)));
model_.set_max_value(0, std::max<float>(x, model_.max_value(0)));
model_.set_max_value(1, std::max<float>(y, model_.max_value(1)));
}
}
}
void FlowQuantizerModel::Init() {
model_.Clear();
// Initialize the values.
for (int i = 0; i != 2; ++i) {
model_.add_min_value(std::numeric_limits<float>::max());
model_.add_max_value(-std::numeric_limits<float>::max());
}
}
} // namespace mediapipe
@@ -0,0 +1,47 @@
// Copyright 2019 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.
// Quantization model to convert a real value float number (flow field) to a
// 8-bit discrete number.
#ifndef MEDIAPIPE_CALCULATORS_VIDEO_TOOL_FLOW_QUANTIZER_MODEL_H_
#define MEDIAPIPE_CALCULATORS_VIDEO_TOOL_FLOW_QUANTIZER_MODEL_H_
#include "mediapipe/calculators/video/tool/flow_quantizer_model.pb.h"
#include "mediapipe/framework/formats/motion/optical_flow_field.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/framework/tool/status_util.h"
namespace mediapipe {
class FlowQuantizerModel {
public:
// Initializes the model proto.
void Init();
// Quantizes flow field with the model.
uint8 Apply(const float val, const int channel) const;
// Loads model from proto.
void LoadFromProto(const QuantizerModelData& data);
// Gets proto from model.
const QuantizerModelData& GetModelData() const;
// Used in training. Updates the model proto by reading the flow fields.
// TODO: This model is currently manually set. Need to find a way to
// learn from flow fields directly.
void AddSampleFlowField(const OpticalFlowField& flow);
private:
QuantizerModelData model_;
};
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_VIDEO_TOOL_FLOW_QUANTIZER_MODEL_H_
@@ -0,0 +1,24 @@
// Copyright 2019 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;
// Message storing min value and max value for normalization in all channels.
message QuantizerModelData {
// For all channels.
repeated float min_value = 1;
repeated float max_value = 2;
}