// 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 #include #include #include #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 absl::Status GetContract(CalculatorContract* cc); absl::Status Open(CalculatorContext* cc) override; absl::Status Process(CalculatorContext* cc) override; private: FlowQuantizerModel model_; }; absl::Status FlowToImageCalculator::GetContract(CalculatorContract* cc) { cc->Inputs().Index(0).Set(); cc->Outputs().Index(0).Set(); // Model sanity check const auto& options = cc->Options(); if (options.min_value() >= options.max_value()) { return absl::InvalidArgumentError("Invalid quantizer model."); } return absl::OkStatus(); } absl::Status FlowToImageCalculator::Open(CalculatorContext* cc) { const auto& options = cc->Options(); // 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( 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 absl::OkStatus(); } absl::Status FlowToImageCalculator::Process(CalculatorContext* cc) { const auto& input = cc->Inputs().Index(0).Get(); // 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_& flow = input.flow_data(); std::unique_ptr 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(j, i) = cv::Vec3b(model_.Apply(flow.at(j, i).x, 0), model_.Apply(flow.at(j, i).y, 1), 0); } } cc->Outputs().Index(0).Add(output.release(), cc->InputTimestamp()); return absl::OkStatus(); } REGISTER_CALCULATOR(FlowToImageCalculator); } // namespace mediapipe