From 1c75e1a56436750c6c536483f26b80e4278eea5d Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:13:46 -0500 Subject: [PATCH 01/16] delete useless code --- sample/src/main.cpp | 260 -------------------- sample/src/object_detection_with_params.cpp | 173 ------------- 2 files changed, 433 deletions(-) delete mode 100644 sample/src/main.cpp delete mode 100644 sample/src/object_detection_with_params.cpp diff --git a/sample/src/main.cpp b/sample/src/main.cpp deleted file mode 100644 index c8478a1c..00000000 --- a/sample/src/main.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ - -/** -* \brief A sample for this library. This sample performs face detection, - * emotions detection, age gender detection and head pose estimation. -* \file sample/main.cpp -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dynamic_vino_lib/common.hpp" -#include "dynamic_vino_lib/engines/engine.hpp" -#include "dynamic_vino_lib/factory.hpp" -#include "dynamic_vino_lib/inferences/age_gender_detection.hpp" -#include "dynamic_vino_lib/inferences/base_inference.hpp" -#include "dynamic_vino_lib/inferences/emotions_detection.hpp" -#include "dynamic_vino_lib/inferences/face_detection.hpp" -#include "dynamic_vino_lib/inferences/head_pose_detection.hpp" -#include "dynamic_vino_lib/inputs/realsense_camera_topic.hpp" -#include "dynamic_vino_lib/outputs/image_window_output.hpp" -#include "dynamic_vino_lib/outputs/ros_topic_output.hpp" -#include "dynamic_vino_lib/pipeline.hpp" -#include "dynamic_vino_lib/slog.hpp" -#include "extension/ext_list.hpp" -#include "gflags/gflags.h" -#include "inference_engine.hpp" -#include "librealsense2/rs.hpp" -#include "opencv2/opencv.hpp" -#include "utility.hpp" - -using namespace InferenceEngine; -using namespace rs2; - -bool parseAndCheckCommandLine(int argc, char** argv) { - // ---------------------------Parsing and validation of input - // args----------------------------- - gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true); - if (FLAGS_h) { - showUsage(); - return false; - } - slog::info << "Parsing input parameters" << slog::endl; - if (FLAGS_i.empty()) { - throw std::logic_error("Parameter -i is not set"); - } - if (!FLAGS_i.compare("Video") && FLAGS_i_path.empty()) { - throw std::logic_error("Parameter -i_path is not set"); - } - if (!FLAGS_i.compare("Image") && FLAGS_i_path.empty()) { - throw std::logic_error("Parameter -i_path is not set"); - } - if (FLAGS_m.empty()) { - throw std::logic_error("Parameter -m is not set"); - } - if (FLAGS_n_ag < 1) { - throw std::logic_error("Parameter -n_ag cannot be 0"); - } - if (FLAGS_n_hp < 1) { - throw std::logic_error("Parameter -n_hp cannot be 0"); - } - return true; -} - -void thread_op(std::shared_ptr node, bool& enable) { - slog::info << "In Input Spin Thread (enable: " << enable << ")" << slog::endl; - while (enable) { - // slog::info << "."; - rclcpp::spin_some(node); - } -} - -int main(int argc, char* argv[]) { - rclcpp::init(argc, argv); - // std::shared_ptr node = - // rclcpp::Node::make_shared("dynamic_vino_sample"); - - try { - std::cout << "InferenceEngine: " << GetInferenceEngineVersion() - << std::endl; - - // ------------------------------ Parsing and validation of input args - // ------------------------- - if (!parseAndCheckCommandLine(argc, argv)) { - return 0; - } - - // --------------------------- 1. Load Plugin for inference engine - // ----------------------------- - std::map plugins_for_devices; - std::vector> cmd_options = { - {FLAGS_d, FLAGS_m}, - {FLAGS_d_ag, FLAGS_m_ag}, - {FLAGS_d_hp, FLAGS_m_hp}, - {FLAGS_d_em, FLAGS_m_em}}; - slog::info << "device_FACE:" << FLAGS_d << slog::endl; - slog::info << "model_FACE:" << FLAGS_m << slog::endl; - slog::info << "device_AG:" << FLAGS_d_ag << slog::endl; - slog::info << "model_AG:" << FLAGS_m_ag << slog::endl; - slog::info << "model_HeadPose:" << FLAGS_m_hp << slog::endl; - slog::info << "device_HeadPose:" << FLAGS_d_hp << slog::endl; - - for (auto&& option : cmd_options) { - auto device_name = option.first; - auto network_name = option.second; - if (device_name.empty() || network_name.empty()) { - continue; - } - if (plugins_for_devices.find(device_name) != plugins_for_devices.end()) { - continue; - } - plugins_for_devices[device_name] = - *Factory::makePluginByName(device_name, FLAGS_l, FLAGS_c, FLAGS_pc); - } - - // --------------------------- 2. Generate Input Device and Output - // Device----------------------- - slog::info << "Reading input" << slog::endl; - auto input_ptr = Factory::makeInputDeviceByName(FLAGS_i, FLAGS_i_path); - // auto input_ptr = std::make_shared(); - - // add node handler to input device instance - // input_ptr->setHandler(node); - if (!input_ptr->initialize()) { - throw std::logic_error("Cannot open input file or camera: " + FLAGS_i); - } - std::string window_name = "Results"; - auto output_ptr = std::make_shared(window_name); - - // --------------------------- 3. Generate Inference - // Instance----------------------------------- - // generate face detection inference - auto face_detection_model = - std::make_shared(FLAGS_m, 1, 1, 1); - face_detection_model->modelInit(); - auto face_detection_engine = std::make_shared( - plugins_for_devices[FLAGS_d], face_detection_model); - auto face_inference_ptr = - std::make_shared(FLAGS_t); - face_inference_ptr->loadNetwork(face_detection_model); - face_inference_ptr->loadEngine(face_detection_engine); - - // generate emotions detection inference - auto emotions_detection_model = - std::make_shared(FLAGS_m_em, 1, 1, 16); - emotions_detection_model->modelInit(); - auto emotions_detection_engine = std::make_shared( - plugins_for_devices[FLAGS_d_em], emotions_detection_model); - auto emotions_inference_ptr = - std::make_shared(); - emotions_inference_ptr->loadNetwork(emotions_detection_model); - emotions_inference_ptr->loadEngine(emotions_detection_engine); - - // generate age gender detection inference - auto agegender_detection_model = - std::make_shared(FLAGS_m_ag, 1, 2, 16); - agegender_detection_model->modelInit(); - auto agegender_detection_engine = std::make_shared( - plugins_for_devices[FLAGS_d_ag], agegender_detection_model); - auto agegender_inference_ptr = - std::make_shared(); - agegender_inference_ptr->loadNetwork(agegender_detection_model); - agegender_inference_ptr->loadEngine(agegender_detection_engine); - - // generate head pose estimation inference - - auto headpose_detection_network = - std::make_shared(FLAGS_m_hp, 1, 3, 16); - headpose_detection_network->modelInit(); - auto headpose_detection_engine = std::make_shared( - plugins_for_devices[FLAGS_d_hp], headpose_detection_network); - auto headpose_inference_ptr = - std::make_shared(); - headpose_inference_ptr->loadNetwork(headpose_detection_network); - headpose_inference_ptr->loadEngine(headpose_detection_engine); - - // --------------------------- 4. Build Pipeline - // -------------------------------------- - Pipeline pipe; - // pipe.add("video_input", std::move(input_ptr)); - pipe.add("video_input", input_ptr); - pipe.add("video_input", "face_detection", face_inference_ptr); - pipe.add("face_detection", "emotions_detection", emotions_inference_ptr); - pipe.add("face_detection", "age_gender_detection", agegender_inference_ptr); - pipe.add("face_detection", "headpose_detection", headpose_inference_ptr); - pipe.add("emotions_detection", "video_output", output_ptr); - pipe.add("age_gender_detection", "video_output", output_ptr); - pipe.add("headpose_detection", "video_output", output_ptr); - pipe.add("face_detection", "video_output", output_ptr); - auto ros_topic_output_ptr = std::make_shared(); - pipe.add("face_detection", "ros_output", ros_topic_output_ptr); - pipe.add("emotions_detection", "ros_output", ros_topic_output_ptr); - pipe.add("age_gender_detection", "ros_output", ros_topic_output_ptr); - pipe.add("headpose_detection", "ros_output", ros_topic_output_ptr); - pipe.setCallback(); - pipe.printPipeline(); - - // auto node = rclcpp::Node::make_shared("sample"); - // bool enable = true; - // std::thread input_spin(thread_op, input_ptr->getHandler(), - // std::ref(enable)); - - slog::info << "Wait 3s before entering data process..." << slog::endl; - sleep(3); - slog::info << "DONE!" << slog::endl; - - // --------------------------- 5. Run Pipeline - // ----------------------------------------- - auto node = input_ptr->getHandler(); - while (cv::waitKey(1) < 0 && cvGetWindowHandle(window_name.c_str())) { - if (node != nullptr) { - rclcpp::spin_some(node); - } - pipe.runOnce(); - if (!FLAGS_i.compare("Image")) { - cv::waitKey(0); - } - } - - // enable = false; - // input_spin.join(); - - slog::info << "Execution successful" << slog::endl; - return 0; - } catch (const std::exception& error) { - slog::err << error.what() << slog::endl; - return 1; - } catch (...) { - slog::err << "Unknown/internal exception happened." << slog::endl; - return 1; - } -} diff --git a/sample/src/object_detection_with_params.cpp b/sample/src/object_detection_with_params.cpp deleted file mode 100644 index dcc77316..00000000 --- a/sample/src/object_detection_with_params.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ -/** -* \brief A sample for this library. This sample performs face detection, - * emotions detection, age gender detection and head pose estimation. -* \file sample/main.cpp -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "dynamic_vino_lib/common.hpp" -#include "dynamic_vino_lib/engines/engine.hpp" -#include "dynamic_vino_lib/factory.hpp" -#include "dynamic_vino_lib/inferences/age_gender_detection.hpp" -#include "dynamic_vino_lib/inferences/base_inference.hpp" -#include "dynamic_vino_lib/inferences/emotions_detection.hpp" -#include "dynamic_vino_lib/inferences/face_detection.hpp" -#include "dynamic_vino_lib/inferences/head_pose_detection.hpp" -#include "dynamic_vino_lib/inputs/realsense_camera_topic.hpp" -#include "dynamic_vino_lib/outputs/image_window_output.hpp" -#include "dynamic_vino_lib/outputs/ros_topic_output.hpp" -#include "dynamic_vino_lib/pipeline.hpp" -#include "dynamic_vino_lib/slog.hpp" -#include "extension/ext_list.hpp" -#include "gflags/gflags.h" -#include "inference_engine.hpp" -#include "librealsense2/rs.hpp" -#include "opencv2/opencv.hpp" -#include "utility.hpp" -using namespace InferenceEngine; -using namespace rs2; -bool parseAndCheckCommandLine(int argc, char** argv) { - // ---------------------------Parsing and validation of input - // args----------------------------- - gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true); - if (FLAGS_h) { - showUsageForObjectDetection(); - return false; - } -/* return true; - slog::info << "Parsing input parameters" << slog::endl; - if (FLAGS_i.empty()) { - throw std::logic_error("Parameter -i is not set"); - } - - if (FLAGS_m.empty()) { - throw std::logic_error("Parameter -m is not set"); - } - */ - return true; -} -int main(int argc, char* argv[]) { - rclcpp::init(argc, argv); - std::string content; - std::string prefix_path; - ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, - &prefix_path); - slog::info << "prefix_path=" << prefix_path << slog::endl; - - try { - std::cout << "InferenceEngine: " << GetInferenceEngineVersion() - << std::endl; - // ------------------------------ Parsing and validation of input args - // ------------------------- - if (!parseAndCheckCommandLine(argc, argv)) { - return 0; - } - if (FLAGS_config.empty()) { - FLAGS_config = - prefix_path + "/share/dynamic_vino_sample/param/pipeline_object.yaml"; - } - Params::ParamManager::getInstance().parse(FLAGS_config); - Params::ParamManager::getInstance().print(); - auto pipelines = Params::ParamManager::getInstance().getPipelines(); - if (pipelines.size() < 1) { - throw std::logic_error("Pipeline parameters should be set!"); - } - - FLAGS_i = pipelines[0].inputs[0]; - FLAGS_m = pipelines[0].infers[0].model; - - // ----------- 1. Load Plugin for inference engine - std::unique_ptr plugin = Factory::makePluginByName( - FLAGS_d, FLAGS_l, FLAGS_c, FLAGS_pc); - - // --------------------------- 2. Generate Input Device and Output - // Device----------------------- - slog::info << "Reading input" << slog::endl; - auto input_ptr = Factory::makeInputDeviceByName(FLAGS_i); - - // add node handler to input device instance - // input_ptr->setHandler(node); - if (!input_ptr->initialize()) { - throw std::logic_error("Cannot open input file or camera: " + FLAGS_i); - } - std::string window_name = "Results"; - auto output_ptr = std::make_shared(window_name); - // --------------------------- 3. Generate Inference - // Instance----------------------------------- - // generate face detection inference - auto model = - std::make_shared(FLAGS_m, 1, 1, 1); - model->modelInit(); - auto engine = std::make_shared(*plugin, model); - auto object_detection_ptr = - std::make_shared(FLAGS_t); - object_detection_ptr->loadNetwork(model); - object_detection_ptr->loadEngine(engine); - slog::info << "Pass Inference Prep." << slog::endl; - // ------- 4. Build Pipeline ------------- - Pipeline pipe("object"); - auto pipeline_params = Params::ParamManager::getInstance().getPipeline("object"); - pipe.setParams(pipeline_params); - pipe.add("video_input", input_ptr); - pipe.add("video_input", "object_detection", object_detection_ptr); - pipe.add("object_detection", "video_output", output_ptr); - auto ros_topic_output_ptr = std::make_shared(); - pipe.add("object_detection", "ros_output", ros_topic_output_ptr); - pipe.setCallback(); - pipe.printPipeline(); - - slog::info << "Pass Pipeline Init." << slog::endl; - - // ------- 5. Run Pipeline ----------- - auto node = input_ptr->getHandler(); - while (cv::waitKey(1) < 0 && cvGetWindowHandle(window_name.c_str())) { - if (node != nullptr) { - rclcpp::spin_some(node); - } - pipe.runOnce(); - if (!FLAGS_i.compare("Image")) { - cv::waitKey(0); - } - } - // enable = false; - // input_spin.join(); - slog::info << "Execution successful" << slog::endl; - return 0; - } catch (const std::exception& error) { - slog::err << error.what() << slog::endl; - return 1; - } catch (...) { - slog::err << "Unknown/internal exception happened." << slog::endl; - return 1; - } -} From 66ac0051b0dcf219fba846fce09a33678ecd7339 Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:17:30 -0500 Subject: [PATCH 02/16] code style reformat --- dynamic_vino_lib/src/engines/engine.cpp | 37 ++-- dynamic_vino_lib/src/factory.cpp | 60 +++--- .../src/inferences/age_gender_detection.cpp | 86 ++++---- .../src/inferences/base_inference.cpp | 66 +++--- .../src/inferences/emotions_detection.cpp | 95 +++++---- .../src/inferences/face_detection.cpp | 40 ++-- .../src/inferences/head_pose_detection.cpp | 91 ++++---- .../src/inferences/object_detection.cpp | 108 ++++++---- .../src/inferences/object_segmentation.cpp | 143 +++++++------ dynamic_vino_lib/src/inputs/image_input.cpp | 46 ++-- .../src/inputs/image_service_input.cpp | 42 ++-- .../src/inputs/realsense_camera.cpp | 55 +++-- .../src/inputs/realsense_camera_topic.cpp | 54 ++--- .../src/inputs/standard_camera.cpp | 44 ++-- dynamic_vino_lib/src/inputs/video_input.cpp | 45 ++-- .../src/models/age_gender_detection_model.cpp | 76 ++++--- dynamic_vino_lib/src/models/base_model.cpp | 68 +++--- .../src/models/emotion_detection_model.cpp | 69 +++--- .../src/models/face_detection_model.cpp | 42 ++-- .../src/models/head_pose_detection_model.cpp | 85 ++++---- .../src/models/object_detection_model.cpp | 82 ++++---- .../src/models/object_segmentation_model.cpp | 86 ++++---- dynamic_vino_lib/src/outputs/base_output.cpp | 43 ++-- .../src/outputs/image_window_output.cpp | 145 ++++++------- .../src/outputs/ros_service_output.cpp | 84 ++++---- .../src/outputs/ros_topic_output.cpp | 97 +++++---- dynamic_vino_lib/src/outputs/rviz_output.cpp | 76 +++---- dynamic_vino_lib/src/pipeline.cpp | 197 +++++++++--------- dynamic_vino_lib/src/pipeline_manager.cpp | 148 ++++++------- dynamic_vino_lib/src/pipeline_params.cpp | 65 +++--- .../src/services/frame_processing_server.cpp | 177 +++++++++------- sample/src/image_object_client.cpp | 45 ++-- sample/src/image_object_server.cpp | 64 ++++-- sample/src/image_people_client.cpp | 56 ++--- sample/src/image_server.cpp | 44 ++-- sample/src/parameters.cpp | 41 ++-- sample/src/pipeline_with_params.cpp | 63 +++--- vino_param_lib/src/param_manager.cpp | 140 +++++++------ 38 files changed, 1560 insertions(+), 1445 deletions(-) diff --git a/dynamic_vino_lib/src/engines/engine.cpp b/dynamic_vino_lib/src/engines/engine.cpp index 18bcf57b..ac78d3de 100644 --- a/dynamic_vino_lib/src/engines/engine.cpp +++ b/dynamic_vino_lib/src/engines/engine.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with definition of Engine class @@ -20,8 +18,9 @@ */ #include "dynamic_vino_lib/engines/engine.hpp" -Engines::Engine::Engine(InferenceEngine::InferencePlugin plg, - const Models::BaseModel::Ptr base_model) { - request_ = (plg.LoadNetwork(base_model->net_reader_->getNetwork(), {})) - .CreateInferRequestPtr(); +Engines::Engine::Engine( + InferenceEngine::InferencePlugin plg, + const Models::BaseModel::Ptr base_model) +{ + request_ = (plg.LoadNetwork(base_model->net_reader_->getNetwork(), {})).CreateInferRequestPtr(); } diff --git a/dynamic_vino_lib/src/factory.cpp b/dynamic_vino_lib/src/factory.cpp index 0a19e509..4b494f30 100644 --- a/dynamic_vino_lib/src/factory.cpp +++ b/dynamic_vino_lib/src/factory.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Factory class @@ -32,8 +30,8 @@ using namespace InferenceEngine; std::shared_ptr Factory::makeInputDeviceByName( - const std::string& input_device_name, const std::string& input_file_path) { - + const std::string & input_device_name, const std::string & input_file_path) +{ std::cout << "InputDvice: " << input_device_name << std::endl; if (input_device_name == "RealSenseCamera") { return std::make_unique(); @@ -51,13 +49,15 @@ std::shared_ptr Factory::makeInputDeviceByName( } } -std::unique_ptr Factory::makePluginByName( - const std::string& device_name, - const std::string& custom_cpu_library_message, // FLAGS_l - const std::string& custom_cldnn_message, // FLAGS_c - bool performance_message) { // FLAGS_pc - InferencePlugin plugin = PluginDispatcher({"../../../lib/intel64", ""}) - .getPluginByDevice(device_name); +std::unique_ptr +Factory::makePluginByName( + const std::string & device_name, + const std::string & custom_cpu_library_message, // FLAGS_l + const std::string & custom_cldnn_message, // FLAGS_c + bool performance_message) +{ // FLAGS_pc + InferencePlugin plugin = + PluginDispatcher({"../../../lib/intel64", ""}).getPluginByDevice(device_name); /** Printing plugin version **/ printPluginVersion(plugin, std::cout); /** Load extensions for the CPU plugin **/ @@ -67,19 +67,15 @@ std::unique_ptr Factory::makePluginByName( // CPU(MKLDNN) extensions are loaded as a shared library and passed as a // pointer to base // extension - auto extension_ptr = make_so_pointer( - custom_cpu_library_message); + auto extension_ptr = make_so_pointer(custom_cpu_library_message); plugin.AddExtension(extension_ptr); } } else if (!custom_cldnn_message.empty()) { // Load Extensions for other plugins not CPU - plugin.SetConfig( - {{PluginConfigParams::KEY_CONFIG_FILE, custom_cldnn_message}}); + plugin.SetConfig({ {PluginConfigParams::KEY_CONFIG_FILE, custom_cldnn_message}}); } if (performance_message) { - plugin.SetConfig( - {{PluginConfigParams::KEY_PERF_COUNT, PluginConfigParams::YES}}); + plugin.SetConfig({ {PluginConfigParams::KEY_PERF_COUNT, PluginConfigParams::YES}}); } - return std::make_unique( - InferenceEngine::InferenceEnginePluginPtr(plugin)); + return std::make_unique(InferenceEngine::InferenceEnginePluginPtr(plugin)); } diff --git a/dynamic_vino_lib/src/inferences/age_gender_detection.cpp b/dynamic_vino_lib/src/inferences/age_gender_detection.cpp index f74e3260..a463ada5 100644 --- a/dynamic_vino_lib/src/inferences/age_gender_detection.cpp +++ b/dynamic_vino_lib/src/inferences/age_gender_detection.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of AgeGenderResult class @@ -26,70 +24,84 @@ #include "dynamic_vino_lib/outputs/base_output.hpp" // AgeGenderResult -dynamic_vino_lib::AgeGenderResult::AgeGenderResult(const cv::Rect& location) - : Result(location){} +dynamic_vino_lib::AgeGenderResult::AgeGenderResult(const cv::Rect & location) +: Result(location) +{ +} // AgeGender Detection dynamic_vino_lib::AgeGenderDetection::AgeGenderDetection() - : dynamic_vino_lib::BaseInference(){} +: dynamic_vino_lib::BaseInference() +{ +} dynamic_vino_lib::AgeGenderDetection::~AgeGenderDetection() = default; void dynamic_vino_lib::AgeGenderDetection::loadNetwork( - std::shared_ptr network) { + std::shared_ptr network) +{ valid_model_ = network; setMaxBatchSize(network->getMaxBatchSize()); } bool dynamic_vino_lib::AgeGenderDetection::enqueue( - const cv::Mat& frame, const cv::Rect& input_frame_loc) { + const cv::Mat & frame, + const cv::Rect & input_frame_loc) +{ if (getEnqueuedNum() == 0) { results_.clear(); } bool succeed = dynamic_vino_lib::BaseInference::enqueue( - frame, input_frame_loc, 1, getResultsLength(), - valid_model_->getInputName()); - if (!succeed) return false; + frame, input_frame_loc, 1, getResultsLength(), valid_model_->getInputName()); + if (!succeed) { + return false; + } Result r(input_frame_loc); results_.emplace_back(r); return true; } -bool dynamic_vino_lib::AgeGenderDetection::submitRequest() { +bool dynamic_vino_lib::AgeGenderDetection::submitRequest() +{ return dynamic_vino_lib::BaseInference::submitRequest(); } -bool dynamic_vino_lib::AgeGenderDetection::fetchResults() { +bool dynamic_vino_lib::AgeGenderDetection::fetchResults() +{ bool can_fetch = dynamic_vino_lib::BaseInference::fetchResults(); - if (!can_fetch) return false; + if (!can_fetch) { + return false; + } auto request = getEngine()->getRequest(); - InferenceEngine::Blob::Ptr genderBlob = - request->GetBlob(valid_model_->getOutputGenderName()); - InferenceEngine::Blob::Ptr ageBlob = - request->GetBlob(valid_model_->getOutputAgeName()); + InferenceEngine::Blob::Ptr genderBlob = request->GetBlob(valid_model_->getOutputGenderName()); + InferenceEngine::Blob::Ptr ageBlob = request->GetBlob(valid_model_->getOutputAgeName()); for (int i = 0; i < results_.size(); ++i) { - results_[i].age_ = ageBlob->buffer().as()[i] * 100; - results_[i].male_prob_ = genderBlob->buffer().as()[i * 2 + 1]; + results_[i].age_ = ageBlob->buffer().as()[i] * 100; + results_[i].male_prob_ = genderBlob->buffer().as()[i * 2 + 1]; } return true; } -const int dynamic_vino_lib::AgeGenderDetection::getResultsLength() const { +const int dynamic_vino_lib::AgeGenderDetection::getResultsLength() const +{ return static_cast(results_.size()); } -const dynamic_vino_lib::Result* -dynamic_vino_lib::AgeGenderDetection::getLocationResult(int idx) const { +const dynamic_vino_lib::Result * +dynamic_vino_lib::AgeGenderDetection::getLocationResult(int idx) const +{ return &(results_[idx]); } -const std::string dynamic_vino_lib::AgeGenderDetection::getName() const { +const std::string dynamic_vino_lib::AgeGenderDetection::getName() const +{ return valid_model_->getModelName(); } const void dynamic_vino_lib::AgeGenderDetection::observeOutput( - const std::shared_ptr& output) { + const std::shared_ptr & output) +{ if (output != nullptr) { output->accept(results_); } diff --git a/dynamic_vino_lib/src/inferences/base_inference.cpp b/dynamic_vino_lib/src/inferences/base_inference.cpp index e0bbc5b4..7e3a1d3f 100644 --- a/dynamic_vino_lib/src/inferences/base_inference.cpp +++ b/dynamic_vino_lib/src/inferences/base_inference.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of BaseInference class @@ -24,7 +22,8 @@ #include "dynamic_vino_lib/inferences/base_inference.hpp" // Result -dynamic_vino_lib::Result::Result(const cv::Rect& location) { +dynamic_vino_lib::Result::Result(const cv::Rect & location) +{ location_ = location; } @@ -33,33 +32,44 @@ dynamic_vino_lib::BaseInference::BaseInference() = default; dynamic_vino_lib::BaseInference::~BaseInference() = default; -void dynamic_vino_lib::BaseInference::loadEngine( - const std::shared_ptr engine) { +void dynamic_vino_lib::BaseInference::loadEngine(const std::shared_ptr engine) +{ engine_ = engine; } -bool dynamic_vino_lib::BaseInference::submitRequest() { - if (engine_->getRequest() == nullptr) return false; - if (!enqueued_frames) return false; +bool dynamic_vino_lib::BaseInference::submitRequest() +{ + if (engine_->getRequest() == nullptr) { + return false; + } + if (!enqueued_frames) { + return false; + } enqueued_frames = 0; results_fetched_ = false; engine_->getRequest()->StartAsync(); return true; } -bool dynamic_vino_lib::BaseInference::SynchronousRequest() { - - if (engine_->getRequest() == nullptr) return false; - if (!enqueued_frames) return false; +bool dynamic_vino_lib::BaseInference::SynchronousRequest() +{ + if (engine_->getRequest() == nullptr) { + return false; + } + if (!enqueued_frames) { + return false; + } enqueued_frames = 0; results_fetched_ = false; engine_->getRequest()->Infer(); return true; } -bool dynamic_vino_lib::BaseInference::fetchResults() { - if (results_fetched_) return false; +bool dynamic_vino_lib::BaseInference::fetchResults() +{ + if (results_fetched_) { + return false; + } results_fetched_ = true; return true; } - diff --git a/dynamic_vino_lib/src/inferences/emotions_detection.cpp b/dynamic_vino_lib/src/inferences/emotions_detection.cpp index b5b424f8..2fcb114c 100644 --- a/dynamic_vino_lib/src/inferences/emotions_detection.cpp +++ b/dynamic_vino_lib/src/inferences/emotions_detection.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of EmotionsDetection class and @@ -28,29 +26,35 @@ #include "dynamic_vino_lib/slog.hpp" // EmotionsResult -dynamic_vino_lib::EmotionsResult::EmotionsResult(const cv::Rect& location) - : Result(location) {} +dynamic_vino_lib::EmotionsResult::EmotionsResult(const cv::Rect & location) +: Result(location) +{ +} // Emotions Detection dynamic_vino_lib::EmotionsDetection::EmotionsDetection() - : dynamic_vino_lib::BaseInference(){} +: dynamic_vino_lib::BaseInference() +{ +} dynamic_vino_lib::EmotionsDetection::~EmotionsDetection() = default; void dynamic_vino_lib::EmotionsDetection::loadNetwork( - const std::shared_ptr network) { + const std::shared_ptr network) +{ valid_model_ = network; setMaxBatchSize(network->getMaxBatchSize()); } bool dynamic_vino_lib::EmotionsDetection::enqueue( - const cv::Mat& frame, const cv::Rect& input_frame_loc) { + const cv::Mat & frame, + const cv::Rect & input_frame_loc) +{ if (getEnqueuedNum() == 0) { results_.clear(); } bool succeed = dynamic_vino_lib::BaseInference::enqueue( - frame, input_frame_loc, 1, getResultsLength(), - valid_model_->getInputName()); + frame, input_frame_loc, 1, getResultsLength(), valid_model_->getInputName()); if (!succeed) { slog::err << "Failed enqueue Emotion frame." << slog::endl; // TODO(weizhi): throw an error here @@ -61,61 +65,66 @@ bool dynamic_vino_lib::EmotionsDetection::enqueue( return true; } -bool dynamic_vino_lib::EmotionsDetection::submitRequest() { +bool dynamic_vino_lib::EmotionsDetection::submitRequest() +{ return dynamic_vino_lib::BaseInference::submitRequest(); } -bool dynamic_vino_lib::EmotionsDetection::fetchResults() { +bool dynamic_vino_lib::EmotionsDetection::fetchResults() +{ bool can_fetch = dynamic_vino_lib::BaseInference::fetchResults(); - if (!can_fetch) return false; + if (!can_fetch) { + return false; + } int label_length = static_cast(valid_model_->getLabels().size()); std::string output_name = valid_model_->getOutputName(); - InferenceEngine::Blob::Ptr emotions_blob = - getEngine()->getRequest()->GetBlob(output_name); + InferenceEngine::Blob::Ptr emotions_blob = getEngine()->getRequest()->GetBlob(output_name); /** emotions vector must have the same size as number of channels in model output. Default output format is NCHW so we check index 1 */ int64 num_of_channels = emotions_blob->getTensorDesc().getDims().at(1); if (num_of_channels != label_length) { - slog::err << "Output size (" << num_of_channels - << ") of the Emotions Recognition network is not equal " - << "to used emotions vector size (" << label_length << ")" - << slog::endl; + slog::err << "Output size (" << num_of_channels << + ") of the Emotions Recognition network is not equal " << + "to used emotions vector size (" << label_length << ")" << slog::endl; throw std::logic_error("Output size (" + std::to_string(num_of_channels) + - ") of the Emotions Recognition network is not equal " - "to used emotions vector size (" + - std::to_string(label_length) + ")"); + ") of the Emotions Recognition network is not equal " + "to used emotions vector size (" + + std::to_string(label_length) + ")"); } /** we identify an index of the most probable emotion in output array for idx image to return appropriate emotion name */ - auto emotions_values = emotions_blob->buffer().as(); + auto emotions_values = emotions_blob->buffer().as(); for (int idx = 0; idx < results_.size(); ++idx) { - auto output_idx_pos = emotions_values + label_length*idx; + auto output_idx_pos = emotions_values + label_length * idx; int64 max_prob_emotion_idx = - std::max_element(output_idx_pos, output_idx_pos + label_length) - - output_idx_pos; + std::max_element(output_idx_pos, output_idx_pos + label_length) - output_idx_pos; results_[idx].label_ = valid_model_->getLabels()[max_prob_emotion_idx]; } return true; } -const int dynamic_vino_lib::EmotionsDetection::getResultsLength() const { +const int dynamic_vino_lib::EmotionsDetection::getResultsLength() const +{ return static_cast(results_.size()); } -const dynamic_vino_lib::Result* -dynamic_vino_lib::EmotionsDetection::getLocationResult(int idx) const { +const dynamic_vino_lib::Result * +dynamic_vino_lib::EmotionsDetection::getLocationResult(int idx) const +{ return &(results_[idx]); } -const std::string dynamic_vino_lib::EmotionsDetection::getName() const { +const std::string dynamic_vino_lib::EmotionsDetection::getName() const +{ return valid_model_->getModelName(); } const void dynamic_vino_lib::EmotionsDetection::observeOutput( - const std::shared_ptr& output) { + const std::shared_ptr & output) +{ if (output != nullptr) { output->accept(results_); } diff --git a/dynamic_vino_lib/src/inferences/face_detection.cpp b/dynamic_vino_lib/src/inferences/face_detection.cpp index cc2baae8..686dc122 100644 --- a/dynamic_vino_lib/src/inferences/face_detection.cpp +++ b/dynamic_vino_lib/src/inferences/face_detection.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of FaceDetection class and @@ -29,11 +27,13 @@ #include "dynamic_vino_lib/slog.hpp" // FaceDetectionResult -dynamic_vino_lib::FaceDetectionResult::FaceDetectionResult( - const cv::Rect& location) - : ObjectDetectionResult(location){} +dynamic_vino_lib::FaceDetectionResult::FaceDetectionResult(const cv::Rect & location) +: ObjectDetectionResult(location) +{ +} // FaceDetection dynamic_vino_lib::FaceDetection::FaceDetection(double show_output_thresh) - : ObjectDetection(show_output_thresh){} - +: ObjectDetection(show_output_thresh) +{ +} diff --git a/dynamic_vino_lib/src/inferences/head_pose_detection.cpp b/dynamic_vino_lib/src/inferences/head_pose_detection.cpp index f0765c09..c9da5cce 100644 --- a/dynamic_vino_lib/src/inferences/head_pose_detection.cpp +++ b/dynamic_vino_lib/src/inferences/head_pose_detection.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of HeadPoseDetection class and @@ -26,73 +24,86 @@ #include "dynamic_vino_lib/outputs/base_output.hpp" // HeadPoseResult -dynamic_vino_lib::HeadPoseResult::HeadPoseResult(const cv::Rect& location) - : Result(location){} +dynamic_vino_lib::HeadPoseResult::HeadPoseResult(const cv::Rect & location) +: Result(location) +{ +} // Head Pose Detection dynamic_vino_lib::HeadPoseDetection::HeadPoseDetection() - : dynamic_vino_lib::BaseInference(){} +: dynamic_vino_lib::BaseInference() +{ +} dynamic_vino_lib::HeadPoseDetection::~HeadPoseDetection() = default; void dynamic_vino_lib::HeadPoseDetection::loadNetwork( - std::shared_ptr network) { + std::shared_ptr network) +{ valid_model_ = network; setMaxBatchSize(network->getMaxBatchSize()); } bool dynamic_vino_lib::HeadPoseDetection::enqueue( - const cv::Mat& frame, const cv::Rect& input_frame_loc) { + const cv::Mat & frame, + const cv::Rect & input_frame_loc) +{ if (getEnqueuedNum() == 0) { results_.clear(); } bool succeed = dynamic_vino_lib::BaseInference::enqueue( - frame, input_frame_loc, 1, getResultsLength(), - valid_model_->getInputName()); - if (!succeed) return false; + frame, input_frame_loc, 1, getResultsLength(), valid_model_->getInputName()); + if (!succeed) { + return false; + } Result r(input_frame_loc); results_.emplace_back(r); return true; } -bool dynamic_vino_lib::HeadPoseDetection::submitRequest() { +bool dynamic_vino_lib::HeadPoseDetection::submitRequest() +{ return dynamic_vino_lib::BaseInference::submitRequest(); } -bool dynamic_vino_lib::HeadPoseDetection::fetchResults() { +bool dynamic_vino_lib::HeadPoseDetection::fetchResults() +{ bool can_fetch = dynamic_vino_lib::BaseInference::fetchResults(); - if (!can_fetch) return false; + if (!can_fetch) { + return false; + } auto request = getEngine()->getRequest(); - InferenceEngine::Blob::Ptr angle_r = - request->GetBlob(valid_model_->getOutputOutputAngleR()); - InferenceEngine::Blob::Ptr angle_p = - request->GetBlob(valid_model_->getOutputOutputAngleP()); - InferenceEngine::Blob::Ptr angle_y = - request->GetBlob(valid_model_->getOutputOutputAngleY()); + InferenceEngine::Blob::Ptr angle_r = request->GetBlob(valid_model_->getOutputOutputAngleR()); + InferenceEngine::Blob::Ptr angle_p = request->GetBlob(valid_model_->getOutputOutputAngleP()); + InferenceEngine::Blob::Ptr angle_y = request->GetBlob(valid_model_->getOutputOutputAngleY()); for (int i = 0; i < getResultsLength(); ++i) { - results_[i].angle_r_ = angle_r->buffer().as()[i]; - results_[i].angle_p_ = angle_p->buffer().as()[i]; - results_[i].angle_y_ = angle_y->buffer().as()[i]; + results_[i].angle_r_ = angle_r->buffer().as()[i]; + results_[i].angle_p_ = angle_p->buffer().as()[i]; + results_[i].angle_y_ = angle_y->buffer().as()[i]; } return true; } -const int dynamic_vino_lib::HeadPoseDetection::getResultsLength() const { +const int dynamic_vino_lib::HeadPoseDetection::getResultsLength() const +{ return static_cast(results_.size()); } -const dynamic_vino_lib::Result* -dynamic_vino_lib::HeadPoseDetection::getLocationResult(int idx) const { +const dynamic_vino_lib::Result * +dynamic_vino_lib::HeadPoseDetection::getLocationResult(int idx) const +{ return &(results_[idx]); } -const std::string dynamic_vino_lib::HeadPoseDetection::getName() const { +const std::string dynamic_vino_lib::HeadPoseDetection::getName() const +{ return valid_model_->getModelName(); } const void dynamic_vino_lib::HeadPoseDetection::observeOutput( - const std::shared_ptr& output) { + const std::shared_ptr & output) +{ if (output != nullptr) { output->accept(results_); } diff --git a/dynamic_vino_lib/src/inferences/object_detection.cpp b/dynamic_vino_lib/src/inferences/object_detection.cpp index f631561d..10eabe1c 100644 --- a/dynamic_vino_lib/src/inferences/object_detection.cpp +++ b/dynamic_vino_lib/src/inferences/object_detection.cpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief a header file with declaration of ObjectDetection class and * ObjectDetectionResult class @@ -25,27 +24,33 @@ #include "dynamic_vino_lib/outputs/base_output.hpp" #include "dynamic_vino_lib/slog.hpp" // ObjectDetectionResult -dynamic_vino_lib::ObjectDetectionResult::ObjectDetectionResult( - const cv::Rect& location) - : Result(location){} +dynamic_vino_lib::ObjectDetectionResult::ObjectDetectionResult(const cv::Rect & location) +: Result(location) +{ +} // ObjectDetection dynamic_vino_lib::ObjectDetection::ObjectDetection(double show_output_thresh) - : show_output_thresh_(show_output_thresh), - dynamic_vino_lib::BaseInference(){} +: show_output_thresh_(show_output_thresh), dynamic_vino_lib::BaseInference() +{ +} dynamic_vino_lib::ObjectDetection::~ObjectDetection() = default; void dynamic_vino_lib::ObjectDetection::loadNetwork( - const std::shared_ptr network) { + const std::shared_ptr network) +{ valid_model_ = network; max_proposal_count_ = network->getMaxProposalCount(); object_size_ = network->getObjectSize(); setMaxBatchSize(network->getMaxBatchSize()); } -bool dynamic_vino_lib::ObjectDetection::enqueue(const cv::Mat& frame, - const cv::Rect& input_frame_loc) { +bool dynamic_vino_lib::ObjectDetection::enqueue( + const cv::Mat & frame, + const cv::Rect & input_frame_loc) +{ width_ = frame.cols; height_ = frame.rows; - if (!dynamic_vino_lib::BaseInference::enqueue( - frame, input_frame_loc, 1, 0, valid_model_->getInputName())) { + if (!dynamic_vino_lib::BaseInference::enqueue(frame, input_frame_loc, 1, 0, + valid_model_->getInputName())) + { return false; } Result r(input_frame_loc); @@ -54,45 +59,51 @@ bool dynamic_vino_lib::ObjectDetection::enqueue(const cv::Mat& frame, return true; } -bool dynamic_vino_lib::ObjectDetection::submitRequest() { +bool dynamic_vino_lib::ObjectDetection::submitRequest() +{ return dynamic_vino_lib::BaseInference::submitRequest(); } -bool dynamic_vino_lib::ObjectDetection::fetchResults() { +bool dynamic_vino_lib::ObjectDetection::fetchResults() +{ bool can_fetch = dynamic_vino_lib::BaseInference::fetchResults(); - if (!can_fetch) return false; + if (!can_fetch) { + return false; + } bool found_result = false; results_.clear(); InferenceEngine::InferRequest::Ptr request = getEngine()->getRequest(); std::string output = valid_model_->getOutputName(); - const float* detections = request->GetBlob(output)->buffer().as(); + const float * detections = request->GetBlob(output)->buffer().as(); for (int i = 0; i < max_proposal_count_; i++) { float image_id = detections[i * object_size_ + 0]; cv::Rect r; auto label_num = static_cast(detections[i * object_size_ + 1]); - std::vector& labels = valid_model_->getLabels(); + std::vector & labels = valid_model_->getLabels(); r.x = static_cast(detections[i * object_size_ + 3] * width_); - if (r.x < 0) + if (r.x < 0) { r.x = 0; + } r.y = static_cast(detections[i * object_size_ + 4] * height_); - if (r.y < 0) + if (r.y < 0) { r.y = 0; + } r.width = static_cast(detections[i * object_size_ + 5] * width_ - r.x); - if (r.width < 0) + if (r.width < 0) { r.width = 0; + } - r.height = - static_cast(detections[i * object_size_ + 6] * height_ - r.y); - if (r.height < 0) + r.height = static_cast(detections[i * object_size_ + 6] * height_ - r.y); + if (r.height < 0) { r.height = 0; + } Result result(r); - result.label_ = label_num < labels.size() - ? labels[label_num] - : std::string("label #") + std::to_string(label_num); + result.label_ = label_num < labels.size() ? labels[label_num] : + std::string("label #") + std::to_string(label_num); result.confidence_ = detections[i * object_size_ + 2]; if (result.confidence_ <= show_output_thresh_) { continue; @@ -103,22 +114,27 @@ bool dynamic_vino_lib::ObjectDetection::fetchResults() { found_result = true; results_.emplace_back(result); } - if (!found_result) results_.clear(); + if (!found_result) { + results_.clear(); + } return true; } -const int dynamic_vino_lib::ObjectDetection::getResultsLength() const { +const int dynamic_vino_lib::ObjectDetection::getResultsLength() const +{ return static_cast(results_.size()); } -const dynamic_vino_lib::Result* -dynamic_vino_lib::ObjectDetection::getLocationResult(int idx) const { +const dynamic_vino_lib::Result * dynamic_vino_lib::ObjectDetection::getLocationResult(int idx) const +{ return &(results_[idx]); } -const std::string dynamic_vino_lib::ObjectDetection::getName() const { +const std::string dynamic_vino_lib::ObjectDetection::getName() const +{ return valid_model_->getModelName(); } -const void dynamic_vino_lib::ObjectDetection::observeOutput( - const std::shared_ptr& output) { +const void +dynamic_vino_lib::ObjectDetection::observeOutput(const std::shared_ptr & output) +{ if (output != nullptr) { output->accept(results_); } diff --git a/dynamic_vino_lib/src/inferences/object_segmentation.cpp b/dynamic_vino_lib/src/inferences/object_segmentation.cpp index 21d2e356..d86ce9f4 100644 --- a/dynamic_vino_lib/src/inferences/object_segmentation.cpp +++ b/dynamic_vino_lib/src/inferences/object_segmentation.cpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief a header file with declaration of ObjectSegmentation class and * ObjectSegmentationResult class @@ -28,31 +27,37 @@ #include "dynamic_vino_lib/slog.hpp" // ObjectSegmentationResult -dynamic_vino_lib::ObjectSegmentationResult::ObjectSegmentationResult( - const cv::Rect& location) - : Result(location){} +dynamic_vino_lib::ObjectSegmentationResult::ObjectSegmentationResult(const cv::Rect & location) +: Result(location) +{ +} // ObjectSegmentation dynamic_vino_lib::ObjectSegmentation::ObjectSegmentation(double show_output_thresh) - : show_output_thresh_(show_output_thresh), - dynamic_vino_lib::BaseInference(){} +: show_output_thresh_(show_output_thresh), dynamic_vino_lib::BaseInference() +{ +} dynamic_vino_lib::ObjectSegmentation::~ObjectSegmentation() = default; void dynamic_vino_lib::ObjectSegmentation::loadNetwork( - const std::shared_ptr network) { + const std::shared_ptr network) +{ valid_model_ = network; setMaxBatchSize(network->getMaxBatchSize()); } -bool dynamic_vino_lib::ObjectSegmentation::enqueue(const cv::Mat& frame, - const cv::Rect& input_frame_loc) { +bool dynamic_vino_lib::ObjectSegmentation::enqueue( + const cv::Mat & frame, + const cv::Rect & input_frame_loc) +{ if (width_ == 0 && height_ == 0) { width_ = frame.cols; height_ = frame.rows; } - if (!dynamic_vino_lib::BaseInference::enqueue( - frame, input_frame_loc, 1, 0, valid_model_->getInputName())) { + if (!dynamic_vino_lib::BaseInference::enqueue(frame, input_frame_loc, 1, 0, + valid_model_->getInputName())) + { return false; } Result r(input_frame_loc); @@ -61,22 +66,26 @@ bool dynamic_vino_lib::ObjectSegmentation::enqueue(const cv::Mat& frame, return true; } -bool dynamic_vino_lib::ObjectSegmentation::submitRequest() { +bool dynamic_vino_lib::ObjectSegmentation::submitRequest() +{ return dynamic_vino_lib::BaseInference::submitRequest(); } -bool dynamic_vino_lib::ObjectSegmentation::fetchResults() { +bool dynamic_vino_lib::ObjectSegmentation::fetchResults() +{ bool can_fetch = dynamic_vino_lib::BaseInference::fetchResults(); - if (!can_fetch) return false; + if (!can_fetch) { + return false; + } bool found_result = false; results_.clear(); InferenceEngine::InferRequest::Ptr request = getEngine()->getRequest(); std::string detection_output = valid_model_->getDetectionOutputName(); std::string mask_output = valid_model_->getMaskOutputName(); const auto do_blob = request->GetBlob(detection_output.c_str()); - const auto do_data = do_blob->buffer().as(); + const auto do_data = do_blob->buffer().as(); const auto masks_blob = request->GetBlob(mask_output.c_str()); - const auto masks_data = masks_blob->buffer().as(); + const auto masks_data = masks_blob->buffer().as(); // amount of elements in each detected box description (batch, label, prob, x1, y1, x2, y2) size_t box_num = masks_blob->dims().at(3); size_t label_num = masks_blob->dims().at(2); @@ -85,54 +94,60 @@ bool dynamic_vino_lib::ObjectSegmentation::fetchResults() { size_t W = masks_blob->dims().at(0); size_t box_stride = W * H * label_num; for (size_t box = 0; box < box_num; ++box) { - float* box_info = do_data + box * box_description_size; - float batch = box_info[0]; - if (batch < 0) break; - float prob = box_info[2]; - if (prob > show_output_thresh_) { - float x1 = std::min(std::max(0.0f, box_info[3] * width_), static_cast(width_)); - float y1 = std::min(std::max(0.0f, box_info[4] * height_), static_cast(height_)); - float x2 = std::min(std::max(0.0f, box_info[5] * width_), static_cast(width_)); - float y2 = std::min(std::max(0.0f, box_info[6] * height_), static_cast(height_)); - int box_width = std::min(static_cast(std::max(0.0f, x2 - x1)), width_); - int box_height = std::min(static_cast(std::max(0.0f, y2 - y1)), height_); - int class_id = static_cast(box_info[1] + 1e-6f); - float* mask_arr = masks_data + box_stride * box + H * W * (class_id - 1); - cv::Mat mask_mat(H, W, CV_32FC1, mask_arr); - cv::Rect roi = cv::Rect( - static_cast(x1), static_cast(y1), box_width, box_height); - cv::Mat resized_mask_mat(box_height, box_width, CV_32FC1); - cv::resize(mask_mat, resized_mask_mat, cv::Size(box_width, box_height)); - Result result(roi); - result.confidence_ = prob; - std::vector& labels = valid_model_->getLabels(); - result.label_ = class_id < labels.size() - ? labels[class_id] - : std::string("label #") + std::to_string(class_id); - result.mask_ = resized_mask_mat; - found_result = true; - results_.emplace_back(result); - } + float * box_info = do_data + box * box_description_size; + float batch = box_info[0]; + if (batch < 0) { + break; + } + float prob = box_info[2]; + if (prob > show_output_thresh_) { + float x1 = std::min(std::max(0.0f, box_info[3] * width_), static_cast(width_)); + float y1 = std::min(std::max(0.0f, box_info[4] * height_), static_cast(height_)); + float x2 = std::min(std::max(0.0f, box_info[5] * width_), static_cast(width_)); + float y2 = std::min(std::max(0.0f, box_info[6] * height_), static_cast(height_)); + int box_width = std::min(static_cast(std::max(0.0f, x2 - x1)), width_); + int box_height = std::min(static_cast(std::max(0.0f, y2 - y1)), height_); + int class_id = static_cast(box_info[1] + 1e-6f); + float * mask_arr = masks_data + box_stride * box + H * W * (class_id - 1); + cv::Mat mask_mat(H, W, CV_32FC1, mask_arr); + cv::Rect roi = cv::Rect(static_cast(x1), static_cast(y1), box_width, box_height); + cv::Mat resized_mask_mat(box_height, box_width, CV_32FC1); + cv::resize(mask_mat, resized_mask_mat, cv::Size(box_width, box_height)); + Result result(roi); + result.confidence_ = prob; + std::vector & labels = valid_model_->getLabels(); + result.label_ = class_id < labels.size() ? labels[class_id] : + std::string("label #") + std::to_string(class_id); + result.mask_ = resized_mask_mat; + found_result = true; + results_.emplace_back(result); + } + } + if (!found_result) { + results_.clear(); } - if (!found_result) results_.clear(); return true; } -const int dynamic_vino_lib::ObjectSegmentation::getResultsLength() const { +const int dynamic_vino_lib::ObjectSegmentation::getResultsLength() const +{ return static_cast(results_.size()); } -const dynamic_vino_lib::Result* -dynamic_vino_lib::ObjectSegmentation::getLocationResult(int idx) const { +const dynamic_vino_lib::Result * +dynamic_vino_lib::ObjectSegmentation::getLocationResult(int idx) const +{ return &(results_[idx]); } -const std::string dynamic_vino_lib::ObjectSegmentation::getName() const { +const std::string dynamic_vino_lib::ObjectSegmentation::getName() const +{ return valid_model_->getModelName(); } const void dynamic_vino_lib::ObjectSegmentation::observeOutput( - const std::shared_ptr& output) { + const std::shared_ptr & output) +{ if (output != nullptr) { output->accept(results_); } diff --git a/dynamic_vino_lib/src/inputs/image_input.cpp b/dynamic_vino_lib/src/inputs/image_input.cpp index da7f32a2..1b4e7609 100644 --- a/dynamic_vino_lib/src/inputs/image_input.cpp +++ b/dynamic_vino_lib/src/inputs/image_input.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Image class @@ -22,9 +20,13 @@ #include #include "dynamic_vino_lib/inputs/image_input.hpp" -Input::Image::Image(const std::string& file) { file_.assign(file); } +Input::Image::Image(const std::string & file) +{ + file_.assign(file); +} -bool Input::Image::initialize() { +bool Input::Image::initialize() +{ setFrameID("image_frame"); image_ = cv::imread(file_); if (image_.data != NULL) { @@ -37,7 +39,8 @@ bool Input::Image::initialize() { return isInit(); } -bool Input::Image::read(cv::Mat* frame) { +bool Input::Image::read(cv::Mat * frame) +{ if (!isInit()) { return false; } @@ -45,8 +48,8 @@ bool Input::Image::read(cv::Mat* frame) { return true; } -bool Input::Image::readService(cv::Mat* frame, std::string config_path) { - +bool Input::Image::readService(cv::Mat * frame, std::string config_path) +{ image_ = cv::imread(config_path); if (image_.data != NULL) { setInitStatus(true); @@ -62,6 +65,7 @@ bool Input::Image::readService(cv::Mat* frame, std::string config_path) { return true; } -void Input::Image::config() { +void Input::Image::config() +{ // TODO(weizhi): config } diff --git a/dynamic_vino_lib/src/inputs/image_service_input.cpp b/dynamic_vino_lib/src/inputs/image_service_input.cpp index 23dfc667..de5700e5 100644 --- a/dynamic_vino_lib/src/inputs/image_service_input.cpp +++ b/dynamic_vino_lib/src/inputs/image_service_input.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Image class @@ -23,9 +21,13 @@ #include "dynamic_vino_lib/inputs/image_service_input.hpp" // Image -Input::ServiceImage::ServiceImage(const std::string& file) { file_.assign(file); } +Input::ServiceImage::ServiceImage(const std::string & file) +{ + file_.assign(file); +} -bool Input::ServiceImage::initialize() { +bool Input::ServiceImage::initialize() +{ image_ = cv::imread(file_); if (image_.data != NULL) { setInitStatus(true); @@ -37,7 +39,8 @@ bool Input::ServiceImage::initialize() { return isInit(); } -bool Input::ServiceImage::read(cv::Mat* frame) { +bool Input::ServiceImage::read(cv::Mat * frame) +{ if (!isInit()) { return false; } @@ -48,6 +51,7 @@ bool Input::ServiceImage::read(cv::Mat* frame) { return true; } -void Input::ServiceImage::config() { +void Input::ServiceImage::config() +{ // TODO(weizhi): config } diff --git a/dynamic_vino_lib/src/inputs/realsense_camera.cpp b/dynamic_vino_lib/src/inputs/realsense_camera.cpp index b2e82a27..6b1ca229 100644 --- a/dynamic_vino_lib/src/inputs/realsense_camera.cpp +++ b/dynamic_vino_lib/src/inputs/realsense_camera.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of RealSenseCamera class @@ -23,7 +21,8 @@ #include "dynamic_vino_lib/slog.hpp" // RealSenseCamera -bool Input::RealSenseCamera::initialize() { +bool Input::RealSenseCamera::initialize() +{ setFrameID("realsense_camera_frame"); cfg_.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8, 30); setInitStatus(pipe_.start(cfg_)); @@ -46,15 +45,15 @@ bool Input::RealSenseCamera::initialize() { } return true; } -bool Input::RealSenseCamera::initialize(size_t width, size_t height) { +bool Input::RealSenseCamera::initialize(size_t width, size_t height) +{ setFrameID("realsense_camera_frame"); if (3 * width != 4 * height) { - slog::err << "The aspect ratio must be 4:3 when using RealSense camera" - << slog::endl; + slog::err << "The aspect ratio must be 4:3 when using RealSense camera" << slog::endl; return false; } - cfg_.enable_stream(RS2_STREAM_COLOR, static_cast(width), - static_cast(height), RS2_FORMAT_BGR8, 30); + cfg_.enable_stream(RS2_STREAM_COLOR, static_cast(width), static_cast(height), + RS2_FORMAT_BGR8, 30); setInitStatus(pipe_.start(cfg_)); setWidth(width); setHeight(height); @@ -75,12 +74,12 @@ bool Input::RealSenseCamera::initialize(size_t width, size_t height) { } return true; } -bool Input::RealSenseCamera::read(cv::Mat* frame) { +bool Input::RealSenseCamera::read(cv::Mat * frame) +{ if (!isInit()) { return false; } - rs2::frameset data = - pipe_.wait_for_frames(); // Wait for next set of frames from the camera + rs2::frameset data = pipe_.wait_for_frames(); // Wait for next set of frames from the camera rs2::frame color_frame; try { color_frame = data.get_color_frame(); @@ -88,11 +87,11 @@ bool Input::RealSenseCamera::read(cv::Mat* frame) { return false; } cv::Mat(cv::Size(static_cast(getWidth()), static_cast(getHeight())), CV_8UC3, - (void*)color_frame.get_data(), cv::Mat::AUTO_STEP) - .copyTo(*frame); + (void *)color_frame.get_data(), cv::Mat::AUTO_STEP) + .copyTo(*frame); return true; } -void Input::RealSenseCamera::config() { +void Input::RealSenseCamera::config() +{ // TODO(weizhi): config } - diff --git a/dynamic_vino_lib/src/inputs/realsense_camera_topic.cpp b/dynamic_vino_lib/src/inputs/realsense_camera_topic.cpp index 27c56834..a151ea93 100644 --- a/dynamic_vino_lib/src/inputs/realsense_camera_topic.cpp +++ b/dynamic_vino_lib/src/inputs/realsense_camera_topic.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of RealSenseCamera class @@ -26,42 +24,46 @@ #define INPUT_TOPIC "/openvino_toolkit/image_raw" -Input::RealSenseCameraTopic::RealSenseCameraTopic() : Node("realsense_topic") { - +Input::RealSenseCameraTopic::RealSenseCameraTopic() +: Node("realsense_topic") +{ } -bool Input::RealSenseCameraTopic::initialize() { +bool Input::RealSenseCameraTopic::initialize() +{ slog::info << "before cameraTOpic init" << slog::endl; std::shared_ptr node(this); setHandler(node); sub_ = this->create_subscription( - "/openvino_toolkit/image_raw", - std::bind(&RealSenseCameraTopic::cb, this, std::placeholders::_1)); + "/openvino_toolkit/image_raw", + std::bind(&RealSenseCameraTopic::cb, this, std::placeholders::_1)); image_count = 0; return true; } -void Input::RealSenseCameraTopic::cb(const sensor_msgs::msg::Image::SharedPtr image_msg) { +void Input::RealSenseCameraTopic::cb(const sensor_msgs::msg::Image::SharedPtr image_msg) +{ slog::info << "Receiving a new image from Camera topic." << slog::endl; setFrameID(image_msg->header.frame_id); image = cv_bridge::toCvCopy(image_msg, "bgr8")->image; ++image_count; } -bool Input::RealSenseCameraTopic::read(cv::Mat* frame) { - - if (image.empty() || image_count <= 0){ +bool Input::RealSenseCameraTopic::read(cv::Mat * frame) +{ + if (image.empty() || image_count <= 0) { slog::warn << "No data received in CameraTopic instance" << slog::endl; return false; } *frame = image; --image_count; - + return true; } -void Input::RealSenseCameraTopic::config() { +void Input::RealSenseCameraTopic::config() +{ // TODO(weizhi): config } diff --git a/dynamic_vino_lib/src/inputs/standard_camera.cpp b/dynamic_vino_lib/src/inputs/standard_camera.cpp index 8dddcc3e..ea96ddb0 100644 --- a/dynamic_vino_lib/src/inputs/standard_camera.cpp +++ b/dynamic_vino_lib/src/inputs/standard_camera.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of StandardCamera class @@ -21,7 +19,8 @@ #include "dynamic_vino_lib/inputs/standard_camera.hpp" // StandardCamera -bool Input::StandardCamera::initialize() { +bool Input::StandardCamera::initialize() +{ setFrameID("standard_camera_frame"); setInitStatus(cap.open(0)); setWidth((size_t)cap.get(CV_CAP_PROP_FRAME_WIDTH)); @@ -29,7 +28,8 @@ bool Input::StandardCamera::initialize() { return isInit(); } -bool Input::StandardCamera::initialize(int camera_num) { +bool Input::StandardCamera::initialize(int camera_num) +{ setFrameID("standard_camera_frame"); setInitStatus(cap.open(camera_num)); setWidth((size_t)cap.get(CV_CAP_PROP_FRAME_WIDTH)); @@ -37,7 +37,8 @@ bool Input::StandardCamera::initialize(int camera_num) { return isInit(); } -bool Input::StandardCamera::initialize(size_t width, size_t height) { +bool Input::StandardCamera::initialize(size_t width, size_t height) +{ setFrameID("standard_camera_frame"); setWidth(width); setHeight(height); @@ -49,7 +50,8 @@ bool Input::StandardCamera::initialize(size_t width, size_t height) { return isInit(); } -bool Input::StandardCamera::read(cv::Mat* frame) { +bool Input::StandardCamera::read(cv::Mat * frame) +{ if (!isInit()) { return false; } @@ -57,7 +59,7 @@ bool Input::StandardCamera::read(cv::Mat* frame) { return cap.retrieve(*frame); } -void Input::StandardCamera::config() { +void Input::StandardCamera::config() +{ // TODO(weizhi): config } - diff --git a/dynamic_vino_lib/src/inputs/video_input.cpp b/dynamic_vino_lib/src/inputs/video_input.cpp index 7aa11ceb..ac51e54c 100644 --- a/dynamic_vino_lib/src/inputs/video_input.cpp +++ b/dynamic_vino_lib/src/inputs/video_input.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Video class @@ -24,9 +22,13 @@ #include "dynamic_vino_lib/inputs/video_input.hpp" // Video -Input::Video::Video(const std::string& video) { video_.assign(video); } +Input::Video::Video(const std::string & video) +{ + video_.assign(video); +} -bool Input::Video::initialize() { +bool Input::Video::initialize() +{ setFrameID("video_frame"); setInitStatus(cap.open(video_)); setWidth((size_t)cap.get(CV_CAP_PROP_FRAME_WIDTH)); @@ -34,7 +36,8 @@ bool Input::Video::initialize() { return isInit(); } -bool Input::Video::initialize(size_t width, size_t height) { +bool Input::Video::initialize(size_t width, size_t height) +{ setFrameID("video_frame"); setWidth(width); setHeight(height); @@ -46,7 +49,8 @@ bool Input::Video::initialize(size_t width, size_t height) { return isInit(); } -bool Input::Video::read(cv::Mat* frame) { +bool Input::Video::read(cv::Mat * frame) +{ if (!isInit()) { return false; } @@ -54,6 +58,7 @@ bool Input::Video::read(cv::Mat* frame) { return cap.retrieve(*frame); } -void Input::Video::config() { +void Input::Video::config() +{ // TODO(weizhi): config } diff --git a/dynamic_vino_lib/src/models/age_gender_detection_model.cpp b/dynamic_vino_lib/src/models/age_gender_detection_model.cpp index eea30c1d..55008e95 100644 --- a/dynamic_vino_lib/src/models/age_gender_detection_model.cpp +++ b/dynamic_vino_lib/src/models/age_gender_detection_model.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of AgeGenderDetectionModel class @@ -24,24 +22,25 @@ #include "dynamic_vino_lib/models/age_gender_detection_model.hpp" #include "dynamic_vino_lib/slog.hpp" - // Validated Age Gender Classification Network Models::AgeGenderDetectionModel::AgeGenderDetectionModel( - const std::string& model_loc, int input_num, int output_num, - int max_batch_size) - : BaseModel(model_loc, input_num, output_num, max_batch_size){} + const std::string & model_loc, + int input_num, int output_num, + int max_batch_size) +: BaseModel(model_loc, input_num, output_num, max_batch_size) +{ +} void Models::AgeGenderDetectionModel::setLayerProperty( - InferenceEngine::CNNNetReader::Ptr net_reader) { + InferenceEngine::CNNNetReader::Ptr net_reader) +{ // set input property - InferenceEngine::InputsDataMap input_info_map( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info_map(net_reader->getNetwork().getInputsInfo()); InferenceEngine::InputInfo::Ptr input_info = input_info_map.begin()->second; input_info->setPrecision(InferenceEngine::Precision::FP32); input_info->setLayout(InferenceEngine::Layout::NCHW); // set output property - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); auto it = output_info_map.begin(); InferenceEngine::DataPtr age_output_ptr = (it++)->second; InferenceEngine::DataPtr gender_output_ptr = (it++)->second; @@ -56,10 +55,10 @@ void Models::AgeGenderDetectionModel::setLayerProperty( } void Models::AgeGenderDetectionModel::checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& net_reader) { + const InferenceEngine::CNNNetReader::Ptr & net_reader) +{ slog::info << "Checking Age Gender Detection outputs" << slog::endl; - InferenceEngine::OutputsDataMap output_info( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info(net_reader->getNetwork().getOutputsInfo()); auto it = output_info.begin(); InferenceEngine::DataPtr age_output_ptr = (it++)->second; InferenceEngine::DataPtr gender_output_ptr = (it++)->second; @@ -68,23 +67,22 @@ void Models::AgeGenderDetectionModel::checkLayerProperty( std::swap(age_output_ptr, gender_output_ptr); } if (age_output_ptr->getCreatorLayer().lock()->type != "Convolution") { - throw std::logic_error("In Age Gender network, age layer (" + - age_output_ptr->getCreatorLayer().lock()->name + - ") should be a Convolution, but was: " + - age_output_ptr->getCreatorLayer().lock()->type); + throw std::logic_error( + "In Age Gender network, age layer (" + age_output_ptr->getCreatorLayer().lock()->name + + ") should be a Convolution, but was: " + + age_output_ptr->getCreatorLayer().lock()->type); } if (gender_output_ptr->getCreatorLayer().lock()->type != "SoftMax") { throw std::logic_error("In Age Gender network, gender layer (" + - gender_output_ptr->getCreatorLayer().lock()->name + - ") should be a SoftMax, but was: " + - gender_output_ptr->getCreatorLayer().lock()->type); + gender_output_ptr->getCreatorLayer().lock()->name + + ") should be a SoftMax, but was: " + + gender_output_ptr->getCreatorLayer().lock()->type); } - slog::info << "Age layer: " << age_output_ptr->getCreatorLayer().lock()->name - << slog::endl; - slog::info << "Gender layer: " - << gender_output_ptr->getCreatorLayer().lock()->name << slog::endl; + slog::info << "Age layer: " << age_output_ptr->getCreatorLayer().lock()->name << slog::endl; + slog::info << "Gender layer: " << gender_output_ptr->getCreatorLayer().lock()->name << slog::endl; } -const std::string Models::AgeGenderDetectionModel::getModelName() const { +const std::string Models::AgeGenderDetectionModel::getModelName() const +{ return "Age Gender Detection"; } diff --git a/dynamic_vino_lib/src/models/base_model.cpp b/dynamic_vino_lib/src/models/base_model.cpp index 9a0f35cc..dab29ef9 100644 --- a/dynamic_vino_lib/src/models/base_model.cpp +++ b/dynamic_vino_lib/src/models/base_model.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of BaseModel class @@ -28,19 +26,22 @@ #include "dynamic_vino_lib/slog.hpp" // Validated Base Network -Models::BaseModel::BaseModel(const std::string& model_loc, int input_num, - int output_num, int max_batch_size) - : input_num_(input_num), - output_num_(output_num), - model_loc_(model_loc), - max_batch_size_(max_batch_size) { +Models::BaseModel::BaseModel( + const std::string & model_loc, int input_num, int output_num, + int max_batch_size) +: input_num_(input_num), + output_num_(output_num), + model_loc_(model_loc), + max_batch_size_(max_batch_size) +{ if (model_loc.empty()) { throw std::logic_error("model file name is empty!"); } net_reader_ = std::make_shared(); } -void Models::BaseModel::modelInit() { +void Models::BaseModel::modelInit() +{ slog::info << "Loading network files" << slog::endl; // Read network model net_reader_->ReadNetwork(model_loc_); @@ -56,32 +57,31 @@ void Models::BaseModel::modelInit() { // Read labels (if any) std::string label_file_name = raw_name + ".labels"; std::ifstream input_file(label_file_name); - std::copy(std::istream_iterator(input_file), - std::istream_iterator(), std::back_inserter(labels_)); + std::copy(std::istream_iterator(input_file), std::istream_iterator(), + std::back_inserter(labels_)); checkNetworkSize(input_num_, output_num_, net_reader_); checkLayerProperty(net_reader_); setLayerProperty(net_reader_); } void Models::BaseModel::checkNetworkSize( - int input_size, int output_size, - InferenceEngine::CNNNetReader::Ptr net_reader) { + int input_size, int output_size, + InferenceEngine::CNNNetReader::Ptr net_reader) +{ // TODO(Houk): Repeat, better removed! // check input size slog::info << "Checking input size" << slog::endl; - InferenceEngine::InputsDataMap input_info( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info(net_reader->getNetwork().getInputsInfo()); if (input_info.size() != input_size) { - throw std::logic_error(getModelName() + - " should have " + std::to_string(input_size)+ " input"); + throw std::logic_error(getModelName() + " should have " + std::to_string(input_size) + " inpu" + "t"); } // check output size slog::info << "Checking output size" << slog::endl; - InferenceEngine::OutputsDataMap output_info( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info(net_reader->getNetwork().getOutputsInfo()); if (output_info.size() != output_size) { - throw std::logic_error(getModelName() + - " should have " + std::to_string(output_size) + " output"); + throw std::logic_error(getModelName() + " should have " + std::to_string(output_size) + " outpu" + "t"); } // InferenceEngine::DataPtr& output_data_ptr = output_info.begin()->second; } diff --git a/dynamic_vino_lib/src/models/emotion_detection_model.cpp b/dynamic_vino_lib/src/models/emotion_detection_model.cpp index e76881d2..455a9097 100644 --- a/dynamic_vino_lib/src/models/emotion_detection_model.cpp +++ b/dynamic_vino_lib/src/models/emotion_detection_model.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of EmotionDetectionModel class @@ -23,25 +21,24 @@ #include "dynamic_vino_lib/models/emotion_detection_model.hpp" #include "dynamic_vino_lib/slog.hpp" - // Validated Emotions Detection Network Models::EmotionDetectionModel::EmotionDetectionModel( - const std::string& model_loc, int input_num, int output_num, - int max_batch_size) - : BaseModel(model_loc, input_num, output_num, max_batch_size){} + const std::string & model_loc, int input_num, + int output_num, int max_batch_size) +: BaseModel(model_loc, input_num, output_num, max_batch_size) +{ +} -void Models::EmotionDetectionModel::setLayerProperty( - InferenceEngine::CNNNetReader::Ptr net_reader) { +void Models::EmotionDetectionModel::setLayerProperty(InferenceEngine::CNNNetReader::Ptr net_reader) +{ // set input property - InferenceEngine::InputsDataMap input_info_map( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info_map(net_reader->getNetwork().getInputsInfo()); InferenceEngine::InputInfo::Ptr input_info = input_info_map.begin()->second; input_info->setPrecision(InferenceEngine::Precision::FP32); input_info->setLayout(InferenceEngine::Layout::NCHW); // set output property - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); - InferenceEngine::DataPtr& output_data_ptr = output_info_map.begin()->second; + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::DataPtr & output_data_ptr = output_info_map.begin()->second; output_data_ptr->setPrecision(InferenceEngine::Precision::FP32); output_data_ptr->setLayout(InferenceEngine::Layout::NCHW); // set input and output layer name @@ -50,23 +47,23 @@ void Models::EmotionDetectionModel::setLayerProperty( } void Models::EmotionDetectionModel::checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& net_reader) { + const InferenceEngine::CNNNetReader::Ptr & net_reader) +{ slog::info << "Checking Emotions Detection outputs" << slog::endl; - InferenceEngine::OutputsDataMap output_info( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info(net_reader->getNetwork().getOutputsInfo()); InferenceEngine::DataPtr emotions_output_ptr = output_info.begin()->second; // output layer should be SoftMax type if (emotions_output_ptr->getCreatorLayer().lock()->type != "SoftMax") { throw std::logic_error("In Emotions Recognition network, Emotion layer (" + - emotions_output_ptr->getCreatorLayer().lock()->name + - ") should be a SoftMax, but was: " + - emotions_output_ptr->getCreatorLayer().lock()->type); + emotions_output_ptr->getCreatorLayer().lock()->name + + ") should be a SoftMax, but was: " + + emotions_output_ptr->getCreatorLayer().lock()->type); } - slog::info << "Emotions layer: " - << emotions_output_ptr->getCreatorLayer().lock()->name - << slog::endl; + slog::info << "Emotions layer: " << emotions_output_ptr->getCreatorLayer().lock()->name << + slog::endl; } -const std::string Models::EmotionDetectionModel::getModelName() const { +const std::string Models::EmotionDetectionModel::getModelName() const +{ return "Emotions Detection"; } diff --git a/dynamic_vino_lib/src/models/face_detection_model.cpp b/dynamic_vino_lib/src/models/face_detection_model.cpp index fc9f1932..07945289 100644 --- a/dynamic_vino_lib/src/models/face_detection_model.cpp +++ b/dynamic_vino_lib/src/models/face_detection_model.cpp @@ -1,19 +1,16 @@ - -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of FaceDetectionModel class @@ -26,11 +23,14 @@ #include "dynamic_vino_lib/slog.hpp" // Validated Face Detection Network -Models::FaceDetectionModel::FaceDetectionModel(const std::string& model_loc, - int input_num, int output_num, - int max_batch_size) - : ObjectDetectionModel(model_loc, input_num, output_num, max_batch_size){} +Models::FaceDetectionModel::FaceDetectionModel( + const std::string & model_loc, int input_num, + int output_num, int max_batch_size) +: ObjectDetectionModel(model_loc, input_num, output_num, max_batch_size) +{ +} -const std::string Models::FaceDetectionModel::getModelName() const { +const std::string Models::FaceDetectionModel::getModelName() const +{ return "Face Detection"; } diff --git a/dynamic_vino_lib/src/models/head_pose_detection_model.cpp b/dynamic_vino_lib/src/models/head_pose_detection_model.cpp index b41d52f3..0b26cae1 100644 --- a/dynamic_vino_lib/src/models/head_pose_detection_model.cpp +++ b/dynamic_vino_lib/src/models/head_pose_detection_model.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of HeadPoseDetectionModel class @@ -25,51 +23,48 @@ #include "dynamic_vino_lib/models/head_pose_detection_model.hpp" #include "dynamic_vino_lib/slog.hpp" - // Validated Head Pose Network Models::HeadPoseDetectionModel::HeadPoseDetectionModel( - const std::string& model_loc, int input_num, int output_num, - int max_batch_size) - : BaseModel(model_loc, input_num, output_num, max_batch_size){} + const std::string & model_loc, int input_num, + int output_num, int max_batch_size) +: BaseModel(model_loc, input_num, output_num, max_batch_size) +{ +} void Models::HeadPoseDetectionModel::checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& net_reader) { + const InferenceEngine::CNNNetReader::Ptr & net_reader) +{ slog::info << "Checking Head Pose network outputs" << slog::endl; - InferenceEngine::OutputsDataMap outputInfo( - net_reader->getNetwork().getOutputsInfo()); - std::map layerNames = {{output_angle_r_, false}, - {output_angle_p_, false}, - {output_angle_y_, false}}; + InferenceEngine::OutputsDataMap outputInfo(net_reader->getNetwork().getOutputsInfo()); + std::map layerNames = { {output_angle_r_, false}, + {output_angle_p_, false}, + {output_angle_y_, false}}; - for (auto&& output : outputInfo) { - InferenceEngine::CNNLayerPtr layer = - output.second->getCreatorLayer().lock(); + for (auto && output : outputInfo) { + InferenceEngine::CNNLayerPtr layer = output.second->getCreatorLayer().lock(); if (layerNames.find(layer->name) == layerNames.end()) { - throw std::logic_error("Head Pose network output layer unknown: " + - layer->name + ", should be " + output_angle_r_ + - " or " + output_angle_p_ + " or " + - output_angle_y_); + throw std::logic_error("Head Pose network output layer unknown: " + layer->name + + ", should be " + output_angle_r_ + " or " + output_angle_p_ + " or " + + output_angle_y_); } if (layer->type != "FullyConnected") { throw std::logic_error("Head Pose network output layer (" + layer->name + - ") has invalid type: " + layer->type + - ", should be FullyConnected"); + ") has invalid type: " + layer->type + ", should be FullyConnected"); } - auto fc = dynamic_cast(layer.get()); + auto fc = dynamic_cast(layer.get()); if (fc->_out_num != 1) { throw std::logic_error("Head Pose network output layer (" + layer->name + - ") has invalid out-size=" + - std::to_string(fc->_out_num) + ", should be 1"); + ") has invalid out-size=" + std::to_string(fc->_out_num) + + ", should be 1"); } layerNames[layer->name] = true; } } -void Models::HeadPoseDetectionModel::setLayerProperty( - InferenceEngine::CNNNetReader::Ptr net_reader) { +void Models::HeadPoseDetectionModel::setLayerProperty(InferenceEngine::CNNNetReader::Ptr net_reader) +{ // set input property - InferenceEngine::InputsDataMap input_info_map( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info_map(net_reader->getNetwork().getInputsInfo()); InferenceEngine::InputInfo::Ptr input_info = input_info_map.begin()->second; input_info->setPrecision(InferenceEngine::Precision::U8); @@ -77,15 +72,15 @@ void Models::HeadPoseDetectionModel::setLayerProperty( input_ = input_info_map.begin()->first; // set output property - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); - for (auto& output : output_info_map){ + for (auto & output : output_info_map) { output.second->setPrecision(InferenceEngine::Precision::FP32); output.second->setLayout(InferenceEngine::Layout::NC); } } -const std::string Models::HeadPoseDetectionModel::getModelName() const { +const std::string Models::HeadPoseDetectionModel::getModelName() const +{ return "Head Pose Network"; } diff --git a/dynamic_vino_lib/src/models/object_detection_model.cpp b/dynamic_vino_lib/src/models/object_detection_model.cpp index fba7904b..48eaba25 100644 --- a/dynamic_vino_lib/src/models/object_detection_model.cpp +++ b/dynamic_vino_lib/src/models/object_detection_model.cpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief a header file with declaration of ObjectDetectionModel class * @file object_detection_model.cpp @@ -21,15 +20,16 @@ #include "dynamic_vino_lib/models/object_detection_model.hpp" #include "dynamic_vino_lib/slog.hpp" // Validated Object Detection Network -Models::ObjectDetectionModel::ObjectDetectionModel(const std::string& model_loc, - int input_num, int output_num, - int max_batch_size) - : BaseModel(model_loc, input_num, output_num, max_batch_size){} -void Models::ObjectDetectionModel::setLayerProperty( - InferenceEngine::CNNNetReader::Ptr net_reader) { +Models::ObjectDetectionModel::ObjectDetectionModel( + const std::string & model_loc, int input_num, + int output_num, int max_batch_size) +: BaseModel(model_loc, input_num, output_num, max_batch_size) +{ +} +void Models::ObjectDetectionModel::setLayerProperty(InferenceEngine::CNNNetReader::Ptr net_reader) +{ // set input property - InferenceEngine::InputsDataMap input_info_map( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info_map(net_reader->getNetwork().getInputsInfo()); if (input_info_map.size() != 1) { throw std::logic_error("This sample accepts networks having only one input"); } @@ -37,12 +37,11 @@ void Models::ObjectDetectionModel::setLayerProperty( input_info->setPrecision(InferenceEngine::Precision::U8); input_info->getInputData()->setLayout(InferenceEngine::Layout::NCHW); // set output property - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); if (output_info_map.size() != 1) { throw std::logic_error("This sample accepts networks having only one output"); } - InferenceEngine::DataPtr& output_data_ptr = output_info_map.begin()->second; + InferenceEngine::DataPtr & output_data_ptr = output_info_map.begin()->second; output_data_ptr->setPrecision(InferenceEngine::Precision::FP32); output_data_ptr->setLayout(InferenceEngine::Layout::NCHW); // set input and output layer name @@ -50,25 +49,25 @@ void Models::ObjectDetectionModel::setLayerProperty( output_ = output_info_map.begin()->first; } void Models::ObjectDetectionModel::checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& net_reader) { + const InferenceEngine::CNNNetReader::Ptr & net_reader) +{ slog::info << "Checking Object Detection outputs" << slog::endl; - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); slog::info << "Checking Object Detection outputs ..." << slog::endl; if (output_info_map.size() != 1) { throw std::logic_error("This sample accepts networks having only one output"); } - InferenceEngine::DataPtr& output_data_ptr = output_info_map.begin()->second; + InferenceEngine::DataPtr & output_data_ptr = output_info_map.begin()->second; output_ = output_info_map.begin()->first; slog::info << "Checking Object Detection output ... Name=" << output_ << slog::endl; const InferenceEngine::CNNLayerPtr output_layer = - net_reader->getNetwork().getLayerByName(output_.c_str()); + net_reader->getNetwork().getLayerByName(output_.c_str()); // output layer should have attribute called num_classes slog::info << "Checking Object Detection num_classes" << slog::endl; if (output_layer->params.find("num_classes") == output_layer->params.end()) { throw std::logic_error("Object Detection network output layer (" + output_ + - ") should have num_classes integer attribute"); + ") should have num_classes integer attribute"); } // class number should be equal to size of label vector // if network has default "background" class, fake is used @@ -83,23 +82,22 @@ void Models::ObjectDetectionModel::checkLayerProperty( } } // last dimension of output layer should be 7 - const InferenceEngine::SizeVector output_dims = - output_data_ptr->getTensorDesc().getDims(); + const InferenceEngine::SizeVector output_dims = output_data_ptr->getTensorDesc().getDims(); max_proposal_count_ = static_cast(output_dims[2]); slog::info << "max proposal count is: " << max_proposal_count_ << slog::endl; object_size_ = static_cast(output_dims[3]); if (object_size_ != 7) { - throw std::logic_error( - "Object Detection network output layer should have 7 as a last " - "dimension"); + throw std::logic_error("Object Detection network output layer should have 7 as a last " + "dimension"); } if (output_dims.size() != 4) { - throw std::logic_error( - "Object Detection network output dimensions not compatible shoulld be 4, " - "but was " + - std::to_string(output_dims.size())); + throw std::logic_error("Object Detection network output dimensions not compatible shoulld be " + "4, " + "but was " + + std::to_string(output_dims.size())); } } -const std::string Models::ObjectDetectionModel::getModelName() const { +const std::string Models::ObjectDetectionModel::getModelName() const +{ return "Object Detection"; } diff --git a/dynamic_vino_lib/src/models/object_segmentation_model.cpp b/dynamic_vino_lib/src/models/object_segmentation_model.cpp index 36fd3e2b..4d67258e 100644 --- a/dynamic_vino_lib/src/models/object_segmentation_model.cpp +++ b/dynamic_vino_lib/src/models/object_segmentation_model.cpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief a header file with declaration of ObjectSegmentationModel class * @file object_detection_model.cpp @@ -22,51 +21,50 @@ #include "dynamic_vino_lib/slog.hpp" // Validated Object Detection Network Models::ObjectSegmentationModel::ObjectSegmentationModel( - const std::string& model_loc, int input_num, int output_num, int max_batch_size) - : BaseModel(model_loc, input_num, output_num, max_batch_size){} + const std::string & model_loc, + int input_num, int output_num, + int max_batch_size) +: BaseModel(model_loc, input_num, output_num, max_batch_size) +{ +} void Models::ObjectSegmentationModel::checkNetworkSize( - int input_size, int output_size, - InferenceEngine::CNNNetReader::Ptr net_reader) { + int input_size, int output_size, InferenceEngine::CNNNetReader::Ptr net_reader) +{ slog::info << "Checking input size" << slog::endl; - InferenceEngine::InputsDataMap input_info( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info(net_reader->getNetwork().getInputsInfo()); if (input_info.size() != input_size) { - throw std::logic_error(getModelName() + - " should have " + std::to_string(input_size)+ " input"); + throw std::logic_error(getModelName() + " should have " + std::to_string(input_size) + " inpu" + "t"); } // check output size slog::info << "Checking output size" << slog::endl; - InferenceEngine::OutputsDataMap output_info( - net_reader->getNetwork().getOutputsInfo()); - if (output_info.size() != output_size && output_info.size() != (output_size-1)) { - throw std::logic_error(getModelName() + - " should have " + std::to_string(output_size) + " output"); + InferenceEngine::OutputsDataMap output_info(net_reader->getNetwork().getOutputsInfo()); + if (output_info.size() != output_size && output_info.size() != (output_size - 1)) { + throw std::logic_error(getModelName() + " should have " + std::to_string(output_size) + " outpu" + "t"); } } void Models::ObjectSegmentationModel::setLayerProperty( - InferenceEngine::CNNNetReader::Ptr net_reader) { + InferenceEngine::CNNNetReader::Ptr net_reader) +{ // set input property - InferenceEngine::InputsDataMap input_info_map( - net_reader->getNetwork().getInputsInfo()); + InferenceEngine::InputsDataMap input_info_map(net_reader->getNetwork().getInputsInfo()); auto inputInfoItem = *input_info_map.begin(); inputInfoItem.second->setPrecision(InferenceEngine::Precision::U8); auto network = net_reader->getNetwork(); try { network.addOutput(std::string("detection_output"), 0); - } - catch (std::exception& error) { - throw std::logic_error(getModelName() + - "is failed when adding detection_output laryer."); + } catch (std::exception & error) { + throw std::logic_error(getModelName() + "is failed when adding detection_output laryer."); } network.setBatchSize(1); - slog::info << "Batch size is " - << std::to_string(net_reader->getNetwork().getBatchSize()) << slog::endl; - InferenceEngine::OutputsDataMap output_info_map( - net_reader->getNetwork().getOutputsInfo()); + slog::info << "Batch size is " << std::to_string(net_reader->getNetwork().getBatchSize()) << + slog::endl; + InferenceEngine::OutputsDataMap output_info_map(net_reader->getNetwork().getOutputsInfo()); for (auto & item : output_info_map) { - item.second->setPrecision(InferenceEngine::Precision::FP32); + item.second->setPrecision(InferenceEngine::Precision::FP32); } auto output_ptr = output_info_map.begin(); input_ = input_info_map.begin()->first; @@ -75,9 +73,10 @@ void Models::ObjectSegmentationModel::setLayerProperty( } void Models::ObjectSegmentationModel::checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& net_reader) { + const InferenceEngine::CNNNetReader::Ptr & net_reader) +{ const InferenceEngine::CNNLayerPtr output_layer = - net_reader->getNetwork().getLayerByName("detection_output"); + net_reader->getNetwork().getLayerByName("detection_output"); const int num_classes = output_layer->GetParamAsInt("num_classes"); slog::info << "Checking Object Segmentation output ... num_classes=" << num_classes << slog::endl; if (getLabels().size() != num_classes) { @@ -89,6 +88,7 @@ void Models::ObjectSegmentationModel::checkLayerProperty( } } -const std::string Models::ObjectSegmentationModel::getModelName() const { +const std::string Models::ObjectSegmentationModel::getModelName() const +{ return "Object Segmentation"; } diff --git a/dynamic_vino_lib/src/outputs/base_output.cpp b/dynamic_vino_lib/src/outputs/base_output.cpp index 600acef1..35be522f 100644 --- a/dynamic_vino_lib/src/outputs/base_output.cpp +++ b/dynamic_vino_lib/src/outputs/base_output.cpp @@ -1,23 +1,22 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 "dynamic_vino_lib/outputs/base_output.hpp" #include "dynamic_vino_lib/pipeline.hpp" -int Outputs::BaseOutput::getFPS() const { +int Outputs::BaseOutput::getFPS() const +{ static int fps = 0; static auto t_start = std::chrono::high_resolution_clock::now(); @@ -29,8 +28,7 @@ int Outputs::BaseOutput::getFPS() const { typedef std::chrono::duration> ms; ms secondDetection = std::chrono::duration_cast(t_end - t_start); - if (secondDetection.count() > 1000) - { + if (secondDetection.count() > 1000) { fps = frame_cnt; frame_cnt = 0; t_start = t_end; @@ -39,14 +37,17 @@ int Outputs::BaseOutput::getFPS() const { return fps; } -void Outputs::BaseOutput::setPipeline(Pipeline* const pipeline){ +void Outputs::BaseOutput::setPipeline(Pipeline * const pipeline) +{ pipeline_ = pipeline; } -Pipeline* Outputs::BaseOutput::getPipeline() const { +Pipeline * Outputs::BaseOutput::getPipeline() const +{ return pipeline_; } -cv::Mat Outputs::BaseOutput::getFrame() const { +cv::Mat Outputs::BaseOutput::getFrame() const +{ return frame_; } diff --git a/dynamic_vino_lib/src/outputs/image_window_output.cpp b/dynamic_vino_lib/src/outputs/image_window_output.cpp index 28a12045..af8c813b 100644 --- a/dynamic_vino_lib/src/outputs/image_window_output.cpp +++ b/dynamic_vino_lib/src/outputs/image_window_output.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of ImageWindowOutput class @@ -27,12 +25,13 @@ #include "dynamic_vino_lib/outputs/image_window_output.hpp" #include "dynamic_vino_lib/pipeline.hpp" -Outputs::ImageWindowOutput::ImageWindowOutput(const std::string& window_name, - int focal_length) - : window_name_(window_name), focal_length_(focal_length) { +Outputs::ImageWindowOutput::ImageWindowOutput(const std::string & window_name, int focal_length) +: window_name_(window_name), focal_length_(focal_length) +{ } -void Outputs::ImageWindowOutput::feedFrame(const cv::Mat& frame) { +void Outputs::ImageWindowOutput::feedFrame(const cv::Mat & frame) +{ // frame_ = frame; frame_ = frame.clone(); if (camera_matrix_.empty()) { @@ -48,13 +47,15 @@ void Outputs::ImageWindowOutput::feedFrame(const cv::Mat& frame) { } void Outputs::ImageWindowOutput::mergeMask( -const std::vector& results) { + const std::vector & results) +{ std::map class_color; for (unsigned i = 0; i < results.size(); i++) { std::string class_label = results[i].getLabel(); - if (class_color.find(class_label) == class_color.end()) + if (class_color.find(class_label) == class_color.end()) { class_color[class_label] = class_color.size(); - auto& color = colors_[class_color[class_label]]; + } + auto & color = colors_[class_color[class_label]]; const float alpha = 0.7f; const float MASK_THRESHOLD = 0.5; @@ -63,24 +64,27 @@ const std::vector& results) { cv::Mat mask = results[i].getMask(); cv::Mat colored_mask(location.height, location.width, frame_.type()); - for (int h = 0; h < mask.size().height; ++h) - for (int w = 0; w < mask.size().width; ++w) - for (int ch = 0; ch < colored_mask.channels(); ++ch) - colored_mask.at(h, w)[ch] = mask.at(h, w) > MASK_THRESHOLD - ? 255 * color[ch] - : roi_img.at(h, w)[ch]; + for (int h = 0; h < mask.size().height; ++h) { + for (int w = 0; w < mask.size().width; ++w) { + for (int ch = 0; ch < colored_mask.channels(); ++ch) { + colored_mask.at(h, w)[ch] = mask.at(h, w) > MASK_THRESHOLD ? + 255 * color[ch] : + roi_img.at(h, w)[ch]; + } + } + } cv::addWeighted(colored_mask, alpha, roi_img, 1.0f - alpha, 0.0f, roi_img); } } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { - slog::err << "the size of Object Segmentation and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of Object Segmentation and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { @@ -98,14 +102,14 @@ void Outputs::ImageWindowOutput::accept( } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { // throw std::logic_error("size is not equal!"); - slog::err << "the size of Face Detection and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of Face Detection and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { @@ -122,14 +126,14 @@ void Outputs::ImageWindowOutput::accept( } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { // throw std::logic_error("size is not equal!"); - slog::err << "the size of Face Detection and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of Face Detection and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { @@ -148,14 +152,14 @@ void Outputs::ImageWindowOutput::accept( } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { // throw std::logic_error("size is not equal!"); - slog::err << "the size of Emotion Detection and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of Emotion Detection and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { @@ -166,22 +170,20 @@ void Outputs::ImageWindowOutput::accept( } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { // throw std::logic_error("size is not equal!"); - slog::err - << "the size of AgeGender Detection and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of AgeGender Detection and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { std::ostringstream ostream; // auto age = results[i].getAge(); - ostream << "[Y" << std::fixed << std::setprecision(0) << results[i].getAge() - << "]"; + ostream << "[Y" << std::fixed << std::setprecision(0) << results[i].getAge() << "]"; outputs_[i].desc += ostream.str(); auto male_prob = results[i].getMaleProbability(); @@ -191,8 +193,10 @@ void Outputs::ImageWindowOutput::accept( } } -cv::Point Outputs::ImageWindowOutput::calcAxis(cv::Mat r, double cx, double cy, - double cz, cv::Point cp) { +cv::Point Outputs::ImageWindowOutput::calcAxis( + cv::Mat r, double cx, double cy, double cz, + cv::Point cp) +{ cv::Mat Axis(3, 1, CV_32F); Axis.at(0) = cx; Axis.at(1) = cy; @@ -201,23 +205,19 @@ cv::Point Outputs::ImageWindowOutput::calcAxis(cv::Mat r, double cx, double cy, o.at(2) = camera_matrix_.at(0); Axis = r * Axis + o; cv::Point point; - point.x = static_cast( - (Axis.at(0) / Axis.at(2) * camera_matrix_.at(0)) + - cp.x); - point.y = static_cast( - (Axis.at(1) / Axis.at(2) * camera_matrix_.at(4)) + - cp.y); + point.x = static_cast((Axis.at(0) / Axis.at(2) * camera_matrix_.at(0)) + + cp.x); + point.y = static_cast((Axis.at(1) / Axis.at(2) * camera_matrix_.at(4)) + + cp.y); return point; } -cv::Mat Outputs::ImageWindowOutput::getRotationTransform(double yaw, - double pitch, - double roll) { +cv::Mat Outputs::ImageWindowOutput::getRotationTransform(double yaw, double pitch, double roll) +{ pitch *= CV_PI / 180.0; yaw *= CV_PI / 180.0; roll *= CV_PI / 180.0; - cv::Matx33f Rx(1, 0, 0, 0, cos(pitch), -sin(pitch), 0, sin(pitch), - cos(pitch)); + cv::Matx33f Rx(1, 0, 0, 0, cos(pitch), -sin(pitch), 0, sin(pitch), cos(pitch)); cv::Matx33f Ry(cos(yaw), 0, -sin(yaw), 0, 1, 0, sin(yaw), 0, cos(yaw)); cv::Matx33f Rz(cos(roll), -sin(roll), 0, sin(roll), cos(roll), 0, 0, 0, 1); auto r = cv::Mat(Rz * Ry * Rx); @@ -225,15 +225,14 @@ cv::Mat Outputs::ImageWindowOutput::getRotationTransform(double yaw, } void Outputs::ImageWindowOutput::accept( - const std::vector& results) { + const std::vector & results) +{ if (outputs_.size() == 0) { initOutputs(results.size()); } if (outputs_.size() != results.size()) { // throw std::logic_error("size is not equal!"); - slog::err - << "the size of HeadPose Detection and Output Vector is not equal!" - << slog::endl; + slog::err << "the size of HeadPose Detection and Output Vector is not equal!" << slog::endl; return; } for (unsigned i = 0; i < results.size(); i++) { @@ -245,8 +244,7 @@ void Outputs::ImageWindowOutput::accept( feedFrame(frame_); cv::Mat r = getRotationTransform(yaw, pitch, roll); cv::Rect location = result.getLocation(); - auto cp = cv::Point(location.x + location.width / 2, - location.y + location.height / 2); + auto cp = cv::Point(location.x + location.width / 2, location.y + location.height / 2); outputs_[i].hp_cp = cp; outputs_[i].hp_x = calcAxis(r, scale, 0, 0, cp); outputs_[i].hp_y = calcAxis(r, 0, -scale, 0, cp); @@ -255,18 +253,19 @@ void Outputs::ImageWindowOutput::accept( } } -void Outputs::ImageWindowOutput::decorateFrame() { +void Outputs::ImageWindowOutput::decorateFrame() +{ if (getPipeline()->getParameters()->isGetFps()) { int fps = getFPS(); std::stringstream ss; ss << "FPS: " << fps; - cv::putText(frame_, ss.str(), cv::Point2f(0, 65), cv::FONT_HERSHEY_TRIPLEX, - 0.5, cv::Scalar(255, 0, 0)); + cv::putText(frame_, ss.str(), cv::Point2f(0, 65), cv::FONT_HERSHEY_TRIPLEX, 0.5, + cv::Scalar(255, 0, 0)); } for (auto o : outputs_) { auto new_y = std::max(15, o.rect.y - 15); - cv::putText(frame_, o.desc, cv::Point2f(o.rect.x, new_y), - cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, o.scalar); + cv::putText(frame_, o.desc, cv::Point2f(o.rect.x, new_y), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, + o.scalar); cv::rectangle(frame_, o.rect, o.scalar, 1); if (o.hp_cp != o.hp_x) { cv::line(frame_, o.hp_cp, o.hp_x, cv::Scalar(0, 0, 255), 2); @@ -282,14 +281,16 @@ void Outputs::ImageWindowOutput::decorateFrame() { outputs_.clear(); } -void Outputs::ImageWindowOutput::handleOutput() { +void Outputs::ImageWindowOutput::handleOutput() +{ cv::namedWindow(window_name_, cv::WINDOW_AUTOSIZE); decorateFrame(); cv::imshow(window_name_, frame_); cv::waitKey(1); } -void Outputs::ImageWindowOutput::initOutputs(unsigned size) { +void Outputs::ImageWindowOutput::initOutputs(unsigned size) +{ outputs_.resize(size); for (unsigned i = 0; i < size; i++) { outputs_[i].desc = ""; diff --git a/dynamic_vino_lib/src/outputs/ros_service_output.cpp b/dynamic_vino_lib/src/outputs/ros_service_output.cpp index 52960edc..d64e0315 100644 --- a/dynamic_vino_lib/src/outputs/ros_service_output.cpp +++ b/dynamic_vino_lib/src/outputs/ros_service_output.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of RosTopicOutput class @@ -25,15 +23,18 @@ #include "dynamic_vino_lib/outputs/ros_service_output.hpp" #include "cv_bridge/cv_bridge.h" -//Outputs::RosServiceOutput::RosServiceOutput() +// Outputs::RosServiceOutput::RosServiceOutput() -void Outputs::RosServiceOutput::feedFrame(const cv::Mat& frame) {frame_ = frame.clone();} +void Outputs::RosServiceOutput::feedFrame(const cv::Mat & frame) +{ + frame_ = frame.clone(); +} void Outputs::RosServiceOutput::accept( - const std::vector& results) { - + const std::vector & results) +{ objects_.clear(); - for (auto& r : results) { + for (auto & r : results) { auto loc = r.getLocation(); object_.roi.x_offset = loc.x; object_.roi.y_offset = loc.y; @@ -46,8 +47,8 @@ void Outputs::RosServiceOutput::accept( } void Outputs::RosServiceOutput::accept( - const std::vector& results) { - + const std::vector & results) +{ for (auto r : results) { // slog::info << ">"; auto loc = r.getLocation(); @@ -57,14 +58,13 @@ void Outputs::RosServiceOutput::accept( face_.roi.height = loc.height; face_.object.object_name = r.getLabel(); face_.object.probability = r.getConfidence(); - //faces_topic_->objects_vector.push_back(face); - //objects_topic_->objects_vector.push_back(face); + // faces_topic_->objects_vector.push_back(face); + // objects_topic_->objects_vector.push_back(face); } } -void Outputs::RosServiceOutput::accept( - const std::vector& results) { - +void Outputs::RosServiceOutput::accept(const std::vector & results) +{ for (auto r : results) { auto loc = r.getLocation(); emotion_.roi.x_offset = loc.x; @@ -72,13 +72,13 @@ void Outputs::RosServiceOutput::accept( emotion_.roi.width = loc.width; emotion_.roi.height = loc.height; emotion_.emotion = r.getLabel(); - //emotions_topic_->emotions.push_back(emotion); + // emotions_topic_->emotions.push_back(emotion); } } void Outputs::RosServiceOutput::accept( - const std::vector& results) { - + const std::vector & results) +{ for (auto r : results) { auto loc = r.getLocation(); ag_.roi.x_offset = loc.x; @@ -87,20 +87,19 @@ void Outputs::RosServiceOutput::accept( ag_.roi.height = loc.height; ag_.age = r.getAge(); auto male_prob = r.getMaleProbability(); - if (male_prob > 0.5){ + if (male_prob > 0.5) { ag_.gender = "Male"; ag_.gender_confidence = male_prob; } else { ag_.gender = "Female"; ag_.gender_confidence = 1.0 - male_prob; } - //age_gender_topic_->objects.push_back(ag); + // age_gender_topic_->objects.push_back(ag); } } -void Outputs::RosServiceOutput::accept( - const std::vector& results) { - +void Outputs::RosServiceOutput::accept(const std::vector & results) +{ for (auto r : results) { auto loc = r.getLocation(); hp_.roi.x_offset = loc.x; @@ -110,26 +109,29 @@ void Outputs::RosServiceOutput::accept( hp_.yaw = r.getAngleY(); hp_.pitch = r.getAngleP(); hp_.roll = r.getAngleR(); - //headpose_topic_->headposes.push_back(hp); + // headpose_topic_->headposes.push_back(hp); } } -//void Outputs::RosServiceOutput::handleOutput() +// void Outputs::RosServiceOutput::handleOutput() -void Outputs::RosServiceOutput::setResponse(std::shared_ptr response) { +void Outputs::RosServiceOutput::setResponse( + std::shared_ptr response) +{ response->objects.objects_vector = objects_; } /** * TODO: implement the value gain */ -std_msgs::msg::Header Outputs::RosServiceOutput::getHeader() { +std_msgs::msg::Header Outputs::RosServiceOutput::getHeader() +{ std_msgs::msg::Header header; header.frame_id = "default_camera"; std::chrono::high_resolution_clock::time_point tp = std::chrono::high_resolution_clock::now(); int64 ns = tp.time_since_epoch().count(); - header.stamp.sec = ns/1000000000; - header.stamp.nanosec = ns%1000000000; + header.stamp.sec = ns / 1000000000; + header.stamp.nanosec = ns % 1000000000; return header; } diff --git a/dynamic_vino_lib/src/outputs/ros_topic_output.cpp b/dynamic_vino_lib/src/outputs/ros_topic_output.cpp index f2e8a579..c3fc62bd 100644 --- a/dynamic_vino_lib/src/outputs/ros_topic_output.cpp +++ b/dynamic_vino_lib/src/outputs/ros_topic_output.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of RosTopicOutput class @@ -27,8 +25,8 @@ #include "dynamic_vino_lib/pipeline.hpp" #include "cv_bridge/cv_bridge.h" - -Outputs::RosTopicOutput::RosTopicOutput() { +Outputs::RosTopicOutput::RosTopicOutput() +{ // rmw_qos_profile_t qos = rmw_qos_profile_default; // qos.depth = 10; // qos.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE; @@ -38,14 +36,14 @@ Outputs::RosTopicOutput::RosTopicOutput() { "/openvino_toolkit/segmented_obejcts", 16); pub_detected_object_ = node_->create_publisher( "/openvino_toolkit/detected_objects", 16); - pub_face_ = node_->create_publisher( - "/openvino_toolkit/faces", 16); - pub_emotion_ = node_->create_publisher( - "/openvino_toolkit/emotions", 16); + pub_face_ = + node_->create_publisher("/openvino_toolkit/faces", 16); + pub_emotion_ = + node_->create_publisher("/openvino_toolkit/emotions", 16); pub_age_gender_ = node_->create_publisher( "/openvino_toolkit/age_genders", 16); - pub_headpose_ = node_->create_publisher( - "/openvino_toolkit/headposes", 16); + pub_headpose_ = + node_->create_publisher("/openvino_toolkit/headposes", 16); emotions_topic_ = nullptr; detected_objects_topic_ = nullptr; faces_topic_ = nullptr; @@ -54,14 +52,17 @@ Outputs::RosTopicOutput::RosTopicOutput() { segmented_objects_topic_ = nullptr; } -void Outputs::RosTopicOutput::feedFrame(const cv::Mat& frame) {frame_ = frame.clone();} - +void Outputs::RosTopicOutput::feedFrame(const cv::Mat & frame) +{ + frame_ = frame.clone(); +} void Outputs::RosTopicOutput::accept( - const std::vector& results) { + const std::vector & results) +{ segmented_objects_topic_ = std::make_shared(); people_msgs::msg::ObjectInMask object; - for (auto& r : results) { + for (auto & r : results) { // slog::info << ">"; auto loc = r.getLocation(); object.roi.x_offset = loc.x; @@ -71,19 +72,21 @@ void Outputs::RosTopicOutput::accept( object.object_name = r.getLabel(); object.probability = r.getConfidence(); cv::Mat mask = r.getMask(); - for (int h = 0; h < mask.size().height; ++h) - for (int w = 0; w < mask.size().width; ++w) - object.mask_array.push_back(mask.at(h, w)); + for (int h = 0; h < mask.size().height; ++h) { + for (int w = 0; w < mask.size().width; ++w) { + object.mask_array.push_back(mask.at(h, w)); + } + } segmented_objects_topic_->objects_vector.push_back(object); } } - void Outputs::RosTopicOutput::accept( - const std::vector& results) { + const std::vector & results) +{ detected_objects_topic_ = std::make_shared(); object_msgs::msg::ObjectInBox object; - for (auto& r : results) { + for (auto & r : results) { // slog::info << ">"; auto loc = r.getLocation(); object.roi.x_offset = loc.x; @@ -96,9 +99,9 @@ void Outputs::RosTopicOutput::accept( } } - void Outputs::RosTopicOutput::accept( - const std::vector& results) { + const std::vector & results) +{ faces_topic_ = std::make_shared(); object_msgs::msg::ObjectInBox face; @@ -116,8 +119,8 @@ void Outputs::RosTopicOutput::accept( } } -void Outputs::RosTopicOutput::accept( - const std::vector& results) { +void Outputs::RosTopicOutput::accept(const std::vector & results) +{ emotions_topic_ = std::make_shared(); people_msgs::msg::Emotion emotion; @@ -133,8 +136,8 @@ void Outputs::RosTopicOutput::accept( } } -void Outputs::RosTopicOutput::accept( - const std::vector& results) { +void Outputs::RosTopicOutput::accept(const std::vector & results) +{ age_gender_topic_ = std::make_shared(); people_msgs::msg::AgeGender ag; @@ -147,7 +150,7 @@ void Outputs::RosTopicOutput::accept( ag.roi.height = loc.height; ag.age = r.getAge(); auto male_prob = r.getMaleProbability(); - if (male_prob > 0.5){ + if (male_prob > 0.5) { ag.gender = "Male"; ag.gender_confidence = male_prob; } else { @@ -158,8 +161,8 @@ void Outputs::RosTopicOutput::accept( } } -void Outputs::RosTopicOutput::accept( - const std::vector& results) { +void Outputs::RosTopicOutput::accept(const std::vector & results) +{ headpose_topic_ = std::make_shared(); people_msgs::msg::HeadPose hp; @@ -176,7 +179,8 @@ void Outputs::RosTopicOutput::accept( } } -void Outputs::RosTopicOutput::handleOutput() { +void Outputs::RosTopicOutput::handleOutput() +{ auto header = getHeader(); if (segmented_objects_topic_ != nullptr) { // slog::info << "publishing faces outputs." << slog::endl; @@ -218,12 +222,13 @@ void Outputs::RosTopicOutput::handleOutput() { /** * TODO: implement the value gain */ -std_msgs::msg::Header Outputs::RosTopicOutput::getHeader() { +std_msgs::msg::Header Outputs::RosTopicOutput::getHeader() +{ std_msgs::msg::Header header; header.frame_id = getPipeline()->getInputDevice()->getFrameID(); std::chrono::high_resolution_clock::time_point tp = std::chrono::high_resolution_clock::now(); int64 ns = tp.time_since_epoch().count(); - header.stamp.sec = ns/1000000000; - header.stamp.nanosec = ns%1000000000; + header.stamp.sec = ns / 1000000000; + header.stamp.nanosec = ns % 1000000000; return header; } diff --git a/dynamic_vino_lib/src/outputs/rviz_output.cpp b/dynamic_vino_lib/src/outputs/rviz_output.cpp index 7b5b191c..6ee1a51f 100644 --- a/dynamic_vino_lib/src/outputs/rviz_output.cpp +++ b/dynamic_vino_lib/src/outputs/rviz_output.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of ImageWindowOutput class @@ -27,67 +25,71 @@ #include "dynamic_vino_lib/pipeline.hpp" #include "dynamic_vino_lib/outputs/rviz_output.hpp" -Outputs::RvizOutput::RvizOutput() { +Outputs::RvizOutput::RvizOutput() +{ node_ = rclcpp::Node::make_shared("image_publisher"); image_topic_ = nullptr; - pub_image_ = node_->create_publisher( - "/openvino_toolkit/images", 16); - image_window_output_ = std::make_shared( - "WindowForRviz", 950); + pub_image_ = node_->create_publisher("/openvino_toolkit/images", 16); + image_window_output_ = std::make_shared("WindowForRviz", 950); } -void Outputs::RvizOutput::feedFrame(const cv::Mat& frame) { +void Outputs::RvizOutput::feedFrame(const cv::Mat & frame) +{ image_window_output_->feedFrame(frame); } -void Outputs::RvizOutput::accept( - const std::vector& results) { +void Outputs::RvizOutput::accept(const std::vector & results) +{ image_window_output_->accept(results); } void Outputs::RvizOutput::accept( - const std::vector& results) { + const std::vector & results) +{ image_window_output_->accept(results); } void Outputs::RvizOutput::accept( - const std::vector& results) { + const std::vector & results) +{ image_window_output_->accept(results); } -void Outputs::RvizOutput::accept( - const std::vector& results) { +void Outputs::RvizOutput::accept(const std::vector & results) +{ image_window_output_->accept(results); } -void Outputs::RvizOutput::accept( - const std::vector& results) { +void Outputs::RvizOutput::accept(const std::vector & results) +{ image_window_output_->accept(results); } -void Outputs::RvizOutput::accept( - const std::vector& results) { +void Outputs::RvizOutput::accept(const std::vector & results) +{ image_window_output_->accept(results); } -void Outputs::RvizOutput::handleOutput() { +void Outputs::RvizOutput::handleOutput() +{ image_window_output_->setPipeline(getPipeline()); image_window_output_->decorateFrame(); cv::Mat frame = image_window_output_->getFrame(); std_msgs::msg::Header header = getHeader(); - std::shared_ptr - cv_ptr = std::make_shared(header, "bgr8", frame); + std::shared_ptr cv_ptr = + std::make_shared(header, "bgr8", frame); image_topic_ = cv_ptr->toImageMsg(); pub_image_->publish(image_topic_); } -std_msgs::msg::Header Outputs::RvizOutput::getHeader() { +std_msgs::msg::Header Outputs::RvizOutput::getHeader() +{ std_msgs::msg::Header header; header.frame_id = getPipeline()->getInputDevice()->getFrameID(); std::chrono::high_resolution_clock::time_point tp = std::chrono::high_resolution_clock::now(); int64 ns = tp.time_since_epoch().count(); - header.stamp.sec = ns/1000000000; - header.stamp.nanosec = ns%1000000000; + header.stamp.sec = ns / 1000000000; + header.stamp.nanosec = ns % 1000000000; return header; } diff --git a/dynamic_vino_lib/src/pipeline.cpp b/dynamic_vino_lib/src/pipeline.cpp index 6e042a0f..5b1a96ff 100644 --- a/dynamic_vino_lib/src/pipeline.cpp +++ b/dynamic_vino_lib/src/pipeline.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Pipeline class @@ -30,15 +28,16 @@ using namespace InferenceEngine; -Pipeline::Pipeline(const std::string& name) { +Pipeline::Pipeline(const std::string & name) +{ if (!name.empty()) { params_ = std::make_shared(name); } counter_ = 0; } -bool Pipeline::add(const std::string& name, - std::shared_ptr input_device) { +bool Pipeline::add(const std::string & name, std::shared_ptr input_device) +{ if (name.empty()) { slog::err << "Item name can't be empty!" << slog::endl; return false; @@ -51,10 +50,11 @@ bool Pipeline::add(const std::string& name, return true; } -bool Pipeline::add(const std::string& parent, const std::string& name, - std::shared_ptr output) { - if (parent.empty() || name.empty() || !isLegalConnect(parent, name) || - output == nullptr) { +bool Pipeline::add( + const std::string & parent, const std::string & name, + std::shared_ptr output) +{ + if (parent.empty() || name.empty() || !isLegalConnect(parent, name) || output == nullptr) { slog::err << "ARGuments ERROR when adding output instance!" << slog::endl; return false; } @@ -68,7 +68,8 @@ bool Pipeline::add(const std::string& parent, const std::string& name, return false; } -bool Pipeline::add(const std::string& parent, const std::string& name) { +bool Pipeline::add(const std::string & parent, const std::string & name) +{ if (isLegalConnect(parent, name)) { addConnect(parent, name); return true; @@ -77,19 +78,18 @@ bool Pipeline::add(const std::string& parent, const std::string& name) { return false; } -bool Pipeline::add(const std::string& name, - std::shared_ptr output) { +bool Pipeline::add(const std::string & name, std::shared_ptr output) +{ if (name.empty()) { slog::err << "Item name can't be empty!" << slog::endl; return false; } std::map>::iterator it = - name_to_output_map_.find(name); + name_to_output_map_.find(name); if (it != name_to_output_map_.end()) { - slog::warn << "inferance instance for [" << name - << "] already exists, update it with new instance." - << slog::endl; + slog::warn << "inferance instance for [" << name << + "] already exists, update it with new instance." << slog::endl; } name_to_output_map_[name] = output; output_names_.insert(name); @@ -99,30 +99,31 @@ bool Pipeline::add(const std::string& name, return true; } -void Pipeline::addConnect(const std::string& parent, const std::string& name) { +void Pipeline::addConnect(const std::string & parent, const std::string & name) +{ std::pair::iterator, - std::multimap::iterator> - ret; + std::multimap::iterator> + ret; ret = next_.equal_range(parent); - for (std::multimap::iterator it = ret.first; - it != ret.second; ++it) { + for (std::multimap::iterator it = ret.first; it != ret.second; ++it) { if (it->second == name) { - slog::warn << "The connect [" << parent << "<-->" << name - << "] already exists." << slog::endl; + slog::warn << "The connect [" << parent << "<-->" << name << "] already exists." << + slog::endl; return; } } - slog::info << "Adding connection into pipeline:[" << parent << "<-->" << name - << "]" << slog::endl; + slog::info << "Adding connection into pipeline:[" << parent << "<-->" << name << "]" << + slog::endl; next_.insert({parent, name}); } -bool Pipeline::add(const std::string& parent, const std::string& name, - std::shared_ptr inference) { +bool Pipeline::add( + const std::string & parent, const std::string & name, + std::shared_ptr inference) +{ if (parent.empty() || name.empty() || !isLegalConnect(parent, name)) { - slog::err << "ARGuments ERROR when adding inference instance!" - << slog::endl; + slog::err << "ARGuments ERROR when adding inference instance!" << slog::endl; return false; } @@ -134,20 +135,20 @@ bool Pipeline::add(const std::string& parent, const std::string& name, return false; } -bool Pipeline::add(const std::string& name, - std::shared_ptr inference) { +bool Pipeline::add( + const std::string & name, + std::shared_ptr inference) +{ if (name.empty()) { slog::err << "Item name can't be empty!" << slog::endl; return false; } - std::map>::iterator it = - name_to_detection_map_.find(name); + std::map>::iterator it = + name_to_detection_map_.find(name); if (it != name_to_detection_map_.end()) { - slog::warn << "inferance instance for [" << name - << "] already exists, update it with new instance." - << slog::endl; + slog::warn << "inferance instance for [" << name << + "] already exists, update it with new instance." << slog::endl; } else { ++total_inference_; } @@ -156,25 +157,23 @@ bool Pipeline::add(const std::string& name, return true; } -bool Pipeline::isLegalConnect(const std::string parent, - const std::string child) { +bool Pipeline::isLegalConnect(const std::string parent, const std::string child) +{ int parent_order = getCatagoryOrder(parent); int child_order = getCatagoryOrder(child); - slog::info << "Checking connection into pipeline:[" << parent << "(" - << parent_order << ")" - << "<-->" << child << "(" << child_order << ")" - << "]" << slog::endl; - return (parent_order != kCatagoryOrder_Unknown) && - (child_order != kCatagoryOrder_Unknown) && + slog::info << "Checking connection into pipeline:[" << parent << "(" << parent_order << ")" << + "<-->" << child << "(" << child_order << ")" << + "]" << slog::endl; + return (parent_order != kCatagoryOrder_Unknown) && (child_order != kCatagoryOrder_Unknown) && (parent_order <= child_order); } -int Pipeline::getCatagoryOrder(const std::string name) { +int Pipeline::getCatagoryOrder(const std::string name) +{ int order = kCatagoryOrder_Unknown; if (name == input_device_name_) { order = kCatagoryOrder_Input; - } else if (name_to_detection_map_.find(name) != - name_to_detection_map_.end()) { + } else if (name_to_detection_map_.find(name) != name_to_detection_map_.end()) { order = kCatagoryOrder_Inference; } else if (name_to_output_map_.find(name) != name_to_output_map_.end()) { order = kCatagoryOrder_Output; @@ -183,7 +182,8 @@ int Pipeline::getCatagoryOrder(const std::string name) { return order; } -void Pipeline::runOnce() { +void Pipeline::runOnce() +{ initInferenceCounter(); if (!input_device_->read(&frame_)) { @@ -193,37 +193,34 @@ void Pipeline::runOnce() { } width_ = frame_.cols; height_ = frame_.rows; - for (auto& pair : name_to_output_map_) { + for (auto & pair : name_to_output_map_) { pair.second->feedFrame(frame_); } auto t0 = std::chrono::high_resolution_clock::now(); - for (auto pos = next_.equal_range(input_device_name_); - pos.first != pos.second; ++pos.first) { + for (auto pos = next_.equal_range(input_device_name_); pos.first != pos.second; ++pos.first) { std::string detection_name = pos.first->second; auto detection_ptr = name_to_detection_map_[detection_name]; - detection_ptr->enqueue(frame_, - cv::Rect(width_ / 2, height_ / 2, width_, height_)); - increaseInferenceCounter(); + detection_ptr->enqueue(frame_, cv::Rect(width_ / 2, height_ / 2, width_, height_)); + increaseInferenceCounter(); detection_ptr->submitRequest(); } std::unique_lock lock(counter_mutex_); - cv_.wait(lock, [self = this]() { return self->counter_ == 0; }); + cv_.wait(lock, [self = this]() {return self->counter_ == 0;}); auto t1 = std::chrono::high_resolution_clock::now(); typedef std::chrono::duration> ms; - for (auto& pair : name_to_output_map_) { + for (auto & pair : name_to_output_map_) { // slog::info << "Handling Output ..." << pair.first << slog::endl; pair.second->handleOutput(); } } -void Pipeline::runService(std::string config_path) { - +void Pipeline::runService(std::string config_path) +{ std::cout << "run service once" << std::endl; initInferenceCounter(); - if (!frame_.empty()) - { + if (!frame_.empty()) { frame_.release(); } @@ -233,7 +230,7 @@ void Pipeline::runService(std::string config_path) { } width_ = frame_.cols; height_ = frame_.rows; - for (auto& pair : name_to_output_map_) { + for (auto & pair : name_to_output_map_) { pair.second->feedFrame(frame_); } @@ -241,53 +238,54 @@ void Pipeline::runService(std::string config_path) { std::string detection_name = pos.first->second; auto detection_ptr = name_to_detection_map_[detection_name]; - detection_ptr->enqueue(frame_, - cv::Rect(width_ / 2, height_ / 2, width_, height_)); + detection_ptr->enqueue(frame_, cv::Rect(width_ / 2, height_ / 2, width_, height_)); detection_ptr->SynchronousRequest(); bool fetch_or_not = detection_ptr->fetchResults(); - for (auto& pair : name_to_output_map_) { + for (auto & pair : name_to_output_map_) { detection_ptr->observeOutput(pair.second); } } -void Pipeline::printPipeline() { - for (auto& current_node : next_) { - printf("%s --> %s\n", current_node.first.c_str(), - current_node.second.c_str()); +void Pipeline::printPipeline() +{ + for (auto & current_node : next_) { + printf("%s --> %s\n", current_node.first.c_str(), current_node.second.c_str()); } } -void Pipeline::setCallback() { +void Pipeline::setCallback() +{ #if 0 if (!input_device_->read(&frame_)) { throw std::logic_error("Failed to get frame from cv::VideoCapture"); } width_ = frame_.cols; height_ = frame_.rows; - for (auto& pair : name_to_output_map_) { + for (auto & pair : name_to_output_map_) { pair.second->feedFrame(frame_); } #endif - for (auto& pair : name_to_detection_map_) { + for (auto & pair : name_to_detection_map_) { std::string detection_name = pair.first; std::function callb; - callb = [ detection_name, self = this ]() { - self->callback(detection_name); - return; - }; + callb = [detection_name, self = this]() + { + self->callback(detection_name); + return; + }; pair.second->getEngine()->getRequest()->SetCompletionCallback(callb); } } -void Pipeline::callback(const std::string& detection_name) { +void Pipeline::callback(const std::string & detection_name) +{ // slog::info<<"Hello callback ----> " << detection_name <fetchResults(); // set output - for (auto pos = next_.equal_range(detection_name); pos.first != pos.second; - ++pos.first) { + for (auto pos = next_.equal_range(detection_name); pos.first != pos.second; ++pos.first) { std::string next_name = pos.first->second; // if next is output, then print if (output_names_.find(next_name) != output_names_.end()) { @@ -299,10 +297,8 @@ void Pipeline::callback(const std::string& detection_name) { if (detection_ptr_iter != name_to_detection_map_.end()) { auto next_detection_ptr = detection_ptr_iter->second; for (size_t i = 0; i < detection_ptr->getResultsLength(); ++i) { - const dynamic_vino_lib::Result* prev_result = - detection_ptr->getLocationResult(i); - auto clippedRect = - prev_result->getLocation() & cv::Rect(0, 0, width_, height_); + const dynamic_vino_lib::Result * prev_result = detection_ptr->getLocationResult(i); + auto clippedRect = prev_result->getLocation() & cv::Rect(0, 0, width_, height_); cv::Mat next_input = frame_(clippedRect); next_detection_ptr->enqueue(next_input, prev_result->getLocation()); } @@ -318,17 +314,20 @@ void Pipeline::callback(const std::string& detection_name) { cv_.notify_all(); } -void Pipeline::initInferenceCounter() { +void Pipeline::initInferenceCounter() +{ std::lock_guard lk(counter_mutex_); counter_ = 0; cv_.notify_all(); } -void Pipeline::increaseInferenceCounter() { +void Pipeline::increaseInferenceCounter() +{ std::lock_guard lk(counter_mutex_); ++counter_; // slog::info << "counter = " << counter_ << slog::endl; } -void Pipeline::decreaseInferenceCounter() { +void Pipeline::decreaseInferenceCounter() +{ std::lock_guard lk(counter_mutex_); --counter_; // slog::info << "counter = " << counter_ << slog::endl; diff --git a/dynamic_vino_lib/src/pipeline_manager.cpp b/dynamic_vino_lib/src/pipeline_manager.cpp index 570e37a9..85822834 100644 --- a/dynamic_vino_lib/src/pipeline_manager.cpp +++ b/dynamic_vino_lib/src/pipeline_manager.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Pipeline Manager class @@ -49,8 +47,9 @@ #include "dynamic_vino_lib/pipeline_manager.hpp" #include "dynamic_vino_lib/pipeline_params.hpp" -std::shared_ptr PipelineManager::createPipeline( - const Params::ParamManager::PipelineParams& params) { +std::shared_ptr +PipelineManager::createPipeline(const Params::ParamManager::PipelineParams & params) +{ if (params.name == "") { throw std::logic_error("The name of pipeline won't be empty!"); } @@ -61,8 +60,7 @@ std::shared_ptr PipelineManager::createPipeline( auto inputs = parseInputDevice(params); if (inputs.size() != 1) { - slog::err << "currently one pipeline only supports ONE input." - << slog::endl; + slog::err << "currently one pipeline only supports ONE input." << slog::endl; return nullptr; } for (auto it = inputs.begin(); it != inputs.end(); ++it) { @@ -99,12 +97,11 @@ std::shared_ptr PipelineManager::createPipeline( return pipeline; } - std::map> -PipelineManager::parseInputDevice( - const Params::ParamManager::PipelineParams& params) { +PipelineManager::parseInputDevice(const Params::ParamManager::PipelineParams & params) +{ std::map> inputs; - for (auto& name : params.inputs) { + for (auto & name : params.inputs) { slog::info << "Parsing InputDvice: " << name << slog::endl; std::shared_ptr device = nullptr; if (name == kInputType_RealSenseCamera) { @@ -136,10 +133,10 @@ PipelineManager::parseInputDevice( } std::map> -PipelineManager::parseOutput( - const Params::ParamManager::PipelineParams& params) { +PipelineManager::parseOutput(const Params::ParamManager::PipelineParams & params) +{ std::map> outputs; - for (auto& name : params.outputs) { + for (auto & name : params.outputs) { slog::info << "Parsing Output: " << name << slog::endl; std::shared_ptr object = nullptr; if (name == kOutputTpye_RosTopic) { @@ -163,17 +160,16 @@ PipelineManager::parseOutput( } std::map> -PipelineManager::parseInference( - const Params::ParamManager::PipelineParams& params) { +PipelineManager::parseInference(const Params::ParamManager::PipelineParams & params) +{ /**< update plugins for devices >**/ auto pcommon = Params::ParamManager::getInstance().getCommon(); std::string FLAGS_l = pcommon.custom_cpu_library; std::string FLAGS_c = pcommon.custom_cldnn_library; bool FLAGS_pc = pcommon.enable_performance_count; - std::map> - inferences; - for (auto& infer : params.infers) { + std::map> inferences; + for (auto & infer : params.infers) { if (infer.name.empty() || infer.model.empty()) { continue; } @@ -181,27 +177,21 @@ PipelineManager::parseInference( std::shared_ptr object = nullptr; if (plugins_for_devices_.find(infer.engine) == plugins_for_devices_.end()) { plugins_for_devices_[infer.engine] = - *Factory::makePluginByName(infer.engine, FLAGS_l, FLAGS_c, FLAGS_pc); + *Factory::makePluginByName(infer.engine, FLAGS_l, FLAGS_c, FLAGS_pc); } if (infer.name == kInferTpye_FaceDetection) { object = createFaceDetection(infer); - } else if (infer.name == kInferTpye_AgeGenderRecognition) { object = createAgeGenderRecognition(infer); - } else if (infer.name == kInferTpye_EmotionRecognition) { object = createEmotionRecognition(infer); - } else if (infer.name == kInferTpye_HeadPoseEstimation) { object = createHeadPoseEstimation(infer); - } else if (infer.name == kInferTpye_ObjectDetection) { object = createObjectDetection(infer); - } else if (infer.name == kInferTpye_ObjectSegmentation) { object = createObjectSegmentation(infer); - } else { slog::err << "Invalid inference name: " << infer.name << slog::endl; } @@ -216,16 +206,15 @@ PipelineManager::parseInference( } std::shared_ptr -PipelineManager::createFaceDetection( - const Params::ParamManager::InferenceParams& infer) { +PipelineManager::createFaceDetection(const Params::ParamManager::InferenceParams & infer) +{ // TODO: add batch size in param_manager - auto face_detection_model = - std::make_shared(infer.model, 1, 1, 1); + auto face_detection_model = std::make_shared(infer.model, 1, 1, 1); face_detection_model->modelInit(); - auto face_detection_engine = std::make_shared( - plugins_for_devices_[infer.engine], face_detection_model); + auto face_detection_engine = + std::make_shared(plugins_for_devices_[infer.engine], face_detection_model); auto face_inference_ptr = std::make_shared( - 0.5); // TODO: add output_threshold in param_manager + 0.5); // TODO: add output_threshold in param_manager face_inference_ptr->loadNetwork(face_detection_model); face_inference_ptr->loadEngine(face_detection_engine); @@ -233,13 +222,11 @@ PipelineManager::createFaceDetection( } std::shared_ptr -PipelineManager::createAgeGenderRecognition( - const Params::ParamManager::InferenceParams& param) { - auto model = - std::make_shared(param.model, 1, 2, 16); +PipelineManager::createAgeGenderRecognition(const Params::ParamManager::InferenceParams & param) +{ + auto model = std::make_shared(param.model, 1, 2, 16); model->modelInit(); - auto engine = std::make_shared( - plugins_for_devices_[param.engine], model); + auto engine = std::make_shared(plugins_for_devices_[param.engine], model); auto infer = std::make_shared(); infer->loadNetwork(model); infer->loadEngine(engine); @@ -248,13 +235,11 @@ PipelineManager::createAgeGenderRecognition( } std::shared_ptr -PipelineManager::createEmotionRecognition( - const Params::ParamManager::InferenceParams& param) { - auto model = - std::make_shared(param.model, 1, 1, 16); +PipelineManager::createEmotionRecognition(const Params::ParamManager::InferenceParams & param) +{ + auto model = std::make_shared(param.model, 1, 1, 16); model->modelInit(); - auto engine = std::make_shared( - plugins_for_devices_[param.engine], model); + auto engine = std::make_shared(plugins_for_devices_[param.engine], model); auto infer = std::make_shared(); infer->loadNetwork(model); infer->loadEngine(engine); @@ -263,13 +248,11 @@ PipelineManager::createEmotionRecognition( } std::shared_ptr -PipelineManager::createHeadPoseEstimation( - const Params::ParamManager::InferenceParams& param) { - auto model = - std::make_shared(param.model, 1, 3, 16); +PipelineManager::createHeadPoseEstimation(const Params::ParamManager::InferenceParams & param) +{ + auto model = std::make_shared(param.model, 1, 3, 16); model->modelInit(); - auto engine = std::make_shared( - plugins_for_devices_[param.engine], model); + auto engine = std::make_shared(plugins_for_devices_[param.engine], model); auto infer = std::make_shared(); infer->loadNetwork(model); infer->loadEngine(engine); @@ -278,33 +261,33 @@ PipelineManager::createHeadPoseEstimation( } std::shared_ptr -PipelineManager::createObjectDetection( - const Params::ParamManager::InferenceParams& infer) { +PipelineManager::createObjectDetection(const Params::ParamManager::InferenceParams & infer) +{ // TODO: not implemented yet return createFaceDetection(infer); } std::shared_ptr -PipelineManager::createObjectSegmentation( - const Params::ParamManager::InferenceParams& infer) { +PipelineManager::createObjectSegmentation(const Params::ParamManager::InferenceParams & infer) +{ auto obejct_segmentation_model = - std::make_shared(infer.model, 1, 2, 1); + std::make_shared(infer.model, 1, 2, 1); obejct_segmentation_model->modelInit(); auto obejct_segmentation_engine = std::make_shared( - plugins_for_devices_[infer.engine], obejct_segmentation_model); - auto segmentation_inference_ptr = - std::make_shared(0.5); + plugins_for_devices_[infer.engine], obejct_segmentation_model); + auto segmentation_inference_ptr = std::make_shared(0.5); segmentation_inference_ptr->loadNetwork(obejct_segmentation_model); segmentation_inference_ptr->loadEngine(obejct_segmentation_engine); return segmentation_inference_ptr; } -void PipelineManager::threadPipeline(const char* name) { - PipelineData& p = pipelines_[name]; +void PipelineManager::threadPipeline(const char * name) +{ + PipelineData & p = pipelines_[name]; while (p.state == PipelineState_ThreadRunning && p.pipeline != nullptr) { - for (auto& node : p.spin_nodes) { + for (auto & node : p.spin_nodes) { rclcpp::spin_some(node); } p.pipeline->runOnce(); @@ -312,20 +295,21 @@ void PipelineManager::threadPipeline(const char* name) { } } -void PipelineManager::runAll() { +void PipelineManager::runAll() +{ for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { if (it->second.state != PipelineState_ThreadRunning) { it->second.state = PipelineState_ThreadRunning; } if (it->second.thread == nullptr) { - it->second.thread = - std::make_shared(&PipelineManager::threadPipeline, this, - it->second.params.name.c_str()); + it->second.thread = std::make_shared(&PipelineManager::threadPipeline, this, + it->second.params.name.c_str()); } } } -void PipelineManager::stopAll() { +void PipelineManager::stopAll() +{ for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { if (it->second.state == PipelineState_ThreadRunning) { it->second.state = PipelineState_ThreadStopped; @@ -333,10 +317,10 @@ void PipelineManager::stopAll() { } } -void PipelineManager::joinAll() { +void PipelineManager::joinAll() +{ for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - if (it->second.thread != nullptr && - it->second.state == PipelineState_ThreadRunning) { + if (it->second.thread != nullptr && it->second.state == PipelineState_ThreadRunning) { it->second.thread->join(); } } diff --git a/dynamic_vino_lib/src/pipeline_params.cpp b/dynamic_vino_lib/src/pipeline_params.cpp index d64d8112..e70b8322 100644 --- a/dynamic_vino_lib/src/pipeline_params.cpp +++ b/dynamic_vino_lib/src/pipeline_params.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Pipeline class @@ -45,15 +43,18 @@ const std::string kInferTpye_HeadPoseEstimation = "HeadPoseEstimation"; const std::string kInferTpye_ObjectDetection = "ObjectDetection"; const std::string kInferTpye_ObjectSegmentation = "ObjectSegmentation"; -PipelineParams::PipelineParams(const std::string& name) { params_.name = name; } +PipelineParams::PipelineParams(const std::string & name) +{ + params_.name = name; +} -PipelineParams::PipelineParams( - const Params::ParamManager::PipelineParams& params) { +PipelineParams::PipelineParams(const Params::ParamManager::PipelineParams & params) +{ params_ = params; } -PipelineParams& PipelineParams::operator=( - const Params::ParamManager::PipelineParams& params) { +PipelineParams & PipelineParams::operator=(const Params::ParamManager::PipelineParams & params) +{ params_.name = params.name; params_.infers = params.infers; params_.inputs = params.inputs; @@ -63,35 +64,37 @@ PipelineParams& PipelineParams::operator=( return *this; } -Params::ParamManager::PipelineParams PipelineParams::getPipeline( - const std::string& name) { +Params::ParamManager::PipelineParams PipelineParams::getPipeline(const std::string & name) +{ return Params::ParamManager::getInstance().getPipeline(name); } -void PipelineParams::update() { +void PipelineParams::update() +{ if (!params_.name.empty()) { params_ = getPipeline(params_.name); } } -void PipelineParams::update( - const Params::ParamManager::PipelineParams& params) { +void PipelineParams::update(const Params::ParamManager::PipelineParams & params) +{ params_ = params; } -bool PipelineParams::isOutputTo(std::string& output) { - if (std::find(params_.outputs.begin(), params_.outputs.end(), output) != - params_.outputs.end()) { +bool PipelineParams::isOutputTo(std::string & output) +{ + if (std::find(params_.outputs.begin(), params_.outputs.end(), output) != params_.outputs.end()) { return true; } return false; } -bool PipelineParams::isGetFps() { +bool PipelineParams::isGetFps() +{ /**< Only "Image" input can't computing FPS >**/ if (params_.inputs.size() == 0) { return false; } - return std::find(params_.inputs.begin(), params_.inputs.end(), - kInputType_Image) == params_.inputs.end(); + return std::find(params_.inputs.begin(), params_.inputs.end(), kInputType_Image) == + params_.inputs.end(); } diff --git a/dynamic_vino_lib/src/services/frame_processing_server.cpp b/dynamic_vino_lib/src/services/frame_processing_server.cpp index e84d84bf..d60b68f1 100644 --- a/dynamic_vino_lib/src/services/frame_processing_server.cpp +++ b/dynamic_vino_lib/src/services/frame_processing_server.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 "dynamic_vino_lib/services/frame_processing_server.hpp" @@ -27,9 +25,13 @@ #include "dynamic_vino_lib/inputs/image_input.hpp" #include "dynamic_vino_lib/slog.hpp" -namespace vino_service { - -FrameProcessingServer::FrameProcessingServer(const std::string service_name, const std::string config_path) : Node("node_with_service") { +namespace vino_service +{ +FrameProcessingServer::FrameProcessingServer( + const std::string service_name, + const std::string config_path) +: Node("node_with_service") +{ Params::ParamManager::getInstance().parse(config_path); Params::ParamManager::getInstance().print(); auto pcommon = Params::ParamManager::getInstance().getCommon(); @@ -39,120 +41,141 @@ FrameProcessingServer::FrameProcessingServer(const std::string service_name, con throw std::logic_error("Pipeline parameters should be set!"); } - for (auto& p : pipelines) { + for (auto & p : pipelines) { PipelineManager::getInstance().createPipeline(p); } - for (auto& p : pipelines) { + for (auto & p : pipelines) { for (unsigned int i = 0; i < p.infers.size(); i++) { - if (!p.infers[i].name.compare("FaceDetection")) - { - face_service_ = create_service("/detect_face", std::bind(&FrameProcessingServer::cbFaceDetection, this, std::placeholders::_1, std::placeholders::_2)); + if (!p.infers[i].name.compare("FaceDetection")) { + face_service_ = create_service( + "/detect_face", std::bind(&FrameProcessingServer::cbFaceDetection, this, + std::placeholders::_1, std::placeholders::_2)); } else if (!p.infers[i].name.compare("AgeGenderRecognition")) { - age_gender_service_ = create_service("/detect_age_gender", std::bind(&FrameProcessingServer::cbAgeGenderRecognition, this, std::placeholders::_1, std::placeholders::_2)); + age_gender_service_ = create_service( + "/detect_age_gender", std::bind(&FrameProcessingServer::cbAgeGenderRecognition, this, + std::placeholders::_1, std::placeholders::_2)); } else if (!p.infers[i].name.compare("EmotionRecognition")) { - emotion_service_ = create_service("/detect_emotion", std::bind(&FrameProcessingServer::cbEmotionRecognition, this, std::placeholders::_1, std::placeholders::_2)); + emotion_service_ = create_service( + "/detect_emotion", std::bind(&FrameProcessingServer::cbEmotionRecognition, this, + std::placeholders::_1, std::placeholders::_2)); } else if (!p.infers[i].name.compare("HeadPoseEstimation")) { - head_pose_service_ = create_service("/detect_head_pose", std::bind(&FrameProcessingServer::cbHeadPoseRecognition, this, std::placeholders::_1, std::placeholders::_2)); + head_pose_service_ = create_service( + "/detect_head_pose", std::bind(&FrameProcessingServer::cbHeadPoseRecognition, this, + std::placeholders::_1, std::placeholders::_2)); } else if (!p.infers[i].name.compare("ObjectDetection")) { - object_service_ = create_service("/detect_object", std::bind(&FrameProcessingServer::cbObjectDetection, this, std::placeholders::_1, std::placeholders::_2)); + object_service_ = create_service( + "/detect_object", std::bind(&FrameProcessingServer::cbObjectDetection, this, + std::placeholders::_1, std::placeholders::_2)); } } } - } -void FrameProcessingServer::cbFaceDetection(const std::shared_ptr request, std::shared_ptr response) { - - /* - std::map pipelines_ = PipelineManager::getInstance().getPipelines(); - for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; - //p.pipeline->runService(request->image_path); - //auto output_handle = p.pipeline->getOutputHandle(); - - for (auto& pair : output_handle) { - if (pair.first.compare("FaceDetection")) { - pair.second -> setResponse(response); - response->objects.inference_time_ms = 11.11; - } +void FrameProcessingServer::cbFaceDetection( + const std::shared_ptr request, + std::shared_ptr response) +{ + /* +std::map pipelines_ = +PipelineManager::getInstance().getPipelines(); +for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { + PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; + //p.pipeline->runService(request->image_path); + //auto output_handle = p.pipeline->getOutputHandle(); + + for (auto& pair : output_handle) { + if (pair.first.compare("FaceDetection")) { + pair.second -> setResponse(response); + response->objects.inference_time_ms = 11.11; } - */ - //p.pipeline->runService(request->image_path); - response->objects.inference_time_ms = 11.11; + } + */ + // p.pipeline->runService(request->image_path); + response->objects.inference_time_ms = 11.11; //} } -void FrameProcessingServer::cbAgeGenderRecognition(const std::shared_ptr request, std::shared_ptr response) { - +void FrameProcessingServer::cbAgeGenderRecognition( + const std::shared_ptr request, + std::shared_ptr response) +{ std::cout << "inside cb" << std::endl; - std::map pipelines_ = PipelineManager::getInstance().getPipelines(); + std::map pipelines_ = + PipelineManager::getInstance().getPipelines(); for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; - p.pipeline->runService(request->image_path); + PipelineManager::PipelineData & p = pipelines_[it->second.params.name.c_str()]; + p.pipeline->runService(request->image_path); auto output_handle = p.pipeline->getOutputHandle(); - for (auto& pair : output_handle) { + for (auto & pair : output_handle) { if (pair.first.compare("AgeGenderRecognition")) { - pair.second -> setResponse(response); + pair.second->setResponse(response); } } - //p.pipeline->runService(request->image_path); + // p.pipeline->runService(request->image_path); } - } -void FrameProcessingServer::cbEmotionRecognition(const std::shared_ptr request, std::shared_ptr response) { - +void FrameProcessingServer::cbEmotionRecognition( + const std::shared_ptr request, + std::shared_ptr response) +{ std::cout << "inside cb" << std::endl; - std::map pipelines_ = PipelineManager::getInstance().getPipelines(); + std::map pipelines_ = + PipelineManager::getInstance().getPipelines(); for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; - p.pipeline->runService(request->image_path); + PipelineManager::PipelineData & p = pipelines_[it->second.params.name.c_str()]; + p.pipeline->runService(request->image_path); auto output_handle = p.pipeline->getOutputHandle(); - for (auto& pair : output_handle) { + for (auto & pair : output_handle) { if (pair.first.compare("EmotionRecognition")) { - pair.second -> setResponse(response); + pair.second->setResponse(response); } } - //p.pipeline->runService(request->image_path); + // p.pipeline->runService(request->image_path); } - } -void FrameProcessingServer::cbHeadPoseRecognition(const std::shared_ptr request, std::shared_ptr response) { - +void FrameProcessingServer::cbHeadPoseRecognition( + const std::shared_ptr request, + std::shared_ptr response) +{ std::cout << "inside cb" << std::endl; - std::map pipelines_ = PipelineManager::getInstance().getPipelines(); + std::map pipelines_ = + PipelineManager::getInstance().getPipelines(); for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; - p.pipeline->runService(request->image_path); + PipelineManager::PipelineData & p = pipelines_[it->second.params.name.c_str()]; + p.pipeline->runService(request->image_path); auto output_handle = p.pipeline->getOutputHandle(); - for (auto& pair : output_handle) { + for (auto & pair : output_handle) { if (pair.first.compare("HeadPoseEstimation")) { - pair.second -> setResponse(response); + pair.second->setResponse(response); } } - //p.pipeline->runService(request->image_path); + // p.pipeline->runService(request->image_path); } - } -void FrameProcessingServer::cbObjectDetection(const std::shared_ptr request, std::shared_ptr response) { - std::map pipelines_ = PipelineManager::getInstance().getPipelines(); +void FrameProcessingServer::cbObjectDetection( + const std::shared_ptr request, + std::shared_ptr response) +{ + std::map pipelines_ = + PipelineManager::getInstance().getPipelines(); for (auto it = pipelines_.begin(); it != pipelines_.end(); ++it) { - PipelineManager::PipelineData& p = pipelines_[it->second.params.name.c_str()]; + PipelineManager::PipelineData & p = pipelines_[it->second.params.name.c_str()]; p.pipeline->runService(request->image_path); auto output_handle = p.pipeline->getOutputHandle(); - for (auto& pair : output_handle) { + for (auto & pair : output_handle) { if (!pair.first.compare("RosService")) { - pair.second -> setResponse(response); + pair.second->setResponse(response); } } } diff --git a/sample/src/image_object_client.cpp b/sample/src/image_object_client.cpp index e3863e8d..e97ab81a 100644 --- a/sample/src/image_object_client.cpp +++ b/sample/src/image_object_client.cpp @@ -1,39 +1,39 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 "dynamic_vino_lib/services/frame_processing_server.hpp" #include #include #include #include +#include +#include + +#include "dynamic_vino_lib/services/frame_processing_server.hpp" -int main(int argc, char ** argv) { +int main(int argc, char ** argv) +{ rclcpp::init(argc, argv); auto node = rclcpp::Node::make_shared("service_example"); if (argc != 2) { - RCLCPP_INFO(node->get_logger(), - "Usage: ros2 run dynamic_vino_sample image_object_client" + RCLCPP_INFO(node->get_logger(), "Usage: ros2 run dynamic_vino_sample image_object_client" ""); return -1; } std::string image_path = argv[1]; - auto client = - node->create_client("/detect_object"); + auto client = node->create_client("/detect_object"); auto request = std::make_shared(); request->image_path = image_path; @@ -47,7 +47,8 @@ int main(int argc, char ** argv) { } auto result = client->async_send_request(request); - if (rclcpp::spin_until_future_complete(node, result) == rclcpp::executor::FutureReturnCode::SUCCESS) + if (rclcpp::spin_until_future_complete(node, result) == + rclcpp::executor::FutureReturnCode::SUCCESS) { auto srv = result.get(); diff --git a/sample/src/image_object_server.cpp b/sample/src/image_object_server.cpp index a6aae111..2ea5fa7b 100644 --- a/sample/src/image_object_server.cpp +++ b/sample/src/image_object_server.cpp @@ -1,23 +1,22 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 #include #include #include +#include #include "dynamic_vino_lib/pipeline_manager.hpp" #include "dynamic_vino_lib/services/frame_processing_server.hpp" @@ -28,23 +27,44 @@ #include "gflags/gflags.h" #include "inference_engine.hpp" #include "extension/ext_list.hpp" +#include "utility.hpp" + +bool parseAndCheckCommandLine(int argc, char ** argv) +{ + // -----Parsing and validation of input args--------------------------- + gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true); + if (FLAGS_h) { + showUsageForParam(); + return false; + } + + return true; +} + +std::string getConfigPath(int argc, char * argv[]) +{ + if (parseAndCheckCommandLine(argc, argv)) { + if (!FLAGS_config.empty()) { + return FLAGS_config; + } + } -std::string getConfigPath(int argc, char * argv[]) { std::string content; std::string prefix_path; - ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, - &prefix_path); - //return prefix_path + "/share/dynamic_vino_sample/param/image_object_server.yaml"; - return prefix_path + "/share/dynamic_vino_sample/param/" + argv[1]; + ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, &prefix_path); + // slog::info << "prefix_path=" << prefix_path << slog::endl; + return prefix_path + "/share/dynamic_vino_sample/param/image_object_server.yaml"; } -int main(int argc, char ** argv) { +int main(int argc, char ** argv) +{ rclcpp::init(argc, argv); std::string config_path = getConfigPath(argc, argv); try { - auto node = std::make_shared("frame_processing_server", config_path); + auto node = std::make_shared("frame_processing_server", + config_path); rclcpp::spin(node); } catch (...) { std::cout << "[ERROR] [frame_processing_server]: " << diff --git a/sample/src/image_people_client.cpp b/sample/src/image_people_client.cpp index c45a7599..604f37a3 100644 --- a/sample/src/image_people_client.cpp +++ b/sample/src/image_people_client.cpp @@ -1,47 +1,47 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 "dynamic_vino_lib/services/frame_processing_server.hpp" #include #include #include +#include +#include + +#include "dynamic_vino_lib/services/frame_processing_server.hpp" -int main(int argc, char ** argv) { +int main(int argc, char ** argv) +{ rclcpp::init(argc, argv); std::cout << "object client" << std::endl; auto node = rclcpp::Node::make_shared("service_example"); - + std::string image_path = "/home/intel/Pictures/timg.jpeg"; std::cout << "******0000" << std::endl; - auto client = - node->create_client("/detect_face"); + auto client = node->create_client("/detect_face"); std::cout << "******face client created" << std::endl; auto request = std::make_shared(); std::cout << "******face request created" << std::endl; - auto client = - node->create_client("/detect_face"); + auto client = node->create_client("/detect_face"); std::cout << "******face client created" << std::endl; auto request = std::make_shared(); std::cout << "******face request created" << std::endl; - //request->image_path = argv[1]; + // request->image_path = argv[1]; request->image_path = image_path; while (!client->wait_for_service(std::chrono::seconds(1))) { @@ -53,16 +53,16 @@ int main(int argc, char ** argv) { } auto result = client->async_send_request(request); - + std::cout << "******result got" << std::endl; - if (rclcpp::spin_until_future_complete(node, result) == rclcpp::executor::FutureReturnCode::SUCCESS) + if (rclcpp::spin_until_future_complete(node, result) == + rclcpp::executor::FutureReturnCode::SUCCESS) { auto srv = result.get(); + std::cout << "******srv got" << std::endl; - std::cout << "******srv got" << std::endl; - - std::cout << "***********" << srv->objects.inference_time_ms << std::endl; + std::cout << "***********" << srv->objects.inference_time_ms << std::endl; } } diff --git a/sample/src/image_server.cpp b/sample/src/image_server.cpp index 500f08d4..b1f7a346 100644 --- a/sample/src/image_server.cpp +++ b/sample/src/image_server.cpp @@ -1,26 +1,24 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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 "dynamic_vino_lib/services/frame_processing_server.hpp" +// Copyright (c) 2018 Intel Corporation +// +// 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 #include #include #include +#include +#include "dynamic_vino_lib/services/frame_processing_server.hpp" #include "dynamic_vino_lib/pipeline_manager.hpp" #include "dynamic_vino_lib/pipeline.hpp" #include "dynamic_vino_lib/slog.hpp" @@ -30,22 +28,24 @@ #include "inference_engine.hpp" #include "extension/ext_list.hpp" -std::string getConfigPath(int argc, char * argv[]) { +std::string getConfigPath(int argc, char * argv[]) +{ std::string content; std::string prefix_path; - ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, - &prefix_path); + ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, &prefix_path); return prefix_path + "/share/dynamic_vino_sample/param/image_object_server.yaml"; } -int main(int argc, char ** argv) { +int main(int argc, char ** argv) +{ rclcpp::init(argc, argv); std::string config_path = getConfigPath(argc, argv); std::cout << "***config path is " << config_path << std::endl; try { - auto node = std::make_shared("frame_processing_server", config_path); + auto node = std::make_shared("frame_processing_server", + config_path); rclcpp::spin(node); } catch (...) { std::cout << "[ERROR] [frame_processing_server]: " << diff --git a/sample/src/parameters.cpp b/sample/src/parameters.cpp index 669a8469..e7f8ff21 100644 --- a/sample/src/parameters.cpp +++ b/sample/src/parameters.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * \brief A sample for vino_param_manager library. This sample performs @@ -21,8 +19,9 @@ * \file sample/parameters.cpp */ -#include +#include #include +#include #include #include #include @@ -31,10 +30,10 @@ #include #include #include -#include #include "utility.hpp" -bool parseAndCheckCommandLine(int argc, char** argv) { +bool parseAndCheckCommandLine(int argc, char ** argv) +{ // ------Parsing and validation of inpuu args---------------------- gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true); if (FLAGS_h) { @@ -50,7 +49,8 @@ bool parseAndCheckCommandLine(int argc, char** argv) { return true; } -int main(int argc, char* argv[]) { +int main(int argc, char * argv[]) +{ try { // ------Parsing and validation of input args--------- if (!parseAndCheckCommandLine(argc, argv)) { @@ -62,8 +62,7 @@ int main(int argc, char* argv[]) { slog::info << "print again, should same as above....." << slog::endl; Params::ParamManager::getInstance().print(); - - } catch (const std::exception& error) { + } catch (const std::exception & error) { slog::err << error.what() << slog::endl; return 1; } catch (...) { diff --git a/sample/src/pipeline_with_params.cpp b/sample/src/pipeline_with_params.cpp index 4a09dbd7..f5d1b4bc 100644 --- a/sample/src/pipeline_with_params.cpp +++ b/sample/src/pipeline_with_params.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * \brief A sample for this library. This sample performs face detection, @@ -20,6 +18,9 @@ * \file sample/main.cpp */ +#include +#include +#include #include #include #include @@ -31,14 +32,11 @@ #include #include #include -#include #include #include #include #include -#include -#include #include "dynamic_vino_lib/pipeline.hpp" #include "dynamic_vino_lib/pipeline_manager.hpp" #include "dynamic_vino_lib/slog.hpp" @@ -49,12 +47,9 @@ #include "opencv2/opencv.hpp" #include "utility.hpp" -using namespace InferenceEngine; -using namespace rs2; - -void signalHandler(int signum) { - slog::warn << "!!!!!!!!!!!Interrupt signal (" << signum - << ") received!!!!!!!!!!!!" << slog::endl; +void signalHandler(int signum) +{ + slog::warn << "!!!!!!!!!!!Interrupt signal (" << signum << ") received!!!!!!!!!!!!" << slog::endl; // cleanup and close up stuff here // terminate program @@ -62,7 +57,8 @@ void signalHandler(int signum) { // exit(signum); } -bool parseAndCheckCommandLine(int argc, char** argv) { +bool parseAndCheckCommandLine(int argc, char ** argv) +{ // -----Parsing and validation of input args--------------------------- gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true); if (FLAGS_h) { @@ -73,7 +69,8 @@ bool parseAndCheckCommandLine(int argc, char** argv) { return true; } -std::string getConfigPath(int argc, char* argv[]) { +std::string getConfigPath(int argc, char * argv[]) +{ if (parseAndCheckCommandLine(argc, argv)) { if (!FLAGS_config.empty()) { return FLAGS_config; @@ -82,21 +79,20 @@ std::string getConfigPath(int argc, char* argv[]) { std::string content; std::string prefix_path; - ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, - &prefix_path); + ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, &prefix_path); // slog::info << "prefix_path=" << prefix_path << slog::endl; return prefix_path + "/share/dynamic_vino_sample/param/pipeline_people.yaml"; } -int main(int argc, char* argv[]) { +int main(int argc, char * argv[]) +{ rclcpp::init(argc, argv); // register signal SIGINT and signal handler signal(SIGINT, signalHandler); try { - std::cout << "InferenceEngine: " << GetInferenceEngineVersion() - << std::endl; + std::cout << "InferenceEngine: " << InferenceEngine::GetInferenceEngineVersion() << std::endl; // ----- Parsing and validation of input args----------------------- @@ -111,14 +107,13 @@ int main(int argc, char* argv[]) { throw std::logic_error("Pipeline parameters should be set!"); } // auto createPipeline = PipelineManager::getInstance().createPipeline; - for (auto& p : pipelines) { + for (auto & p : pipelines) { PipelineManager::getInstance().createPipeline(p); } PipelineManager::getInstance().runAll(); PipelineManager::getInstance().joinAll(); - - } catch (const std::exception& error) { + } catch (const std::exception & error) { slog::err << error.what() << slog::endl; return 1; } catch (...) { diff --git a/vino_param_lib/src/param_manager.cpp b/vino_param_lib/src/param_manager.cpp index 6e9ba9a6..2075ba9c 100644 --- a/vino_param_lib/src/param_manager.cpp +++ b/vino_param_lib/src/param_manager.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 "vino_param_lib/param_manager.hpp" #include @@ -23,30 +21,33 @@ #include #include -namespace Params { - -void operator>>(const YAML::Node& node, ParamManager::PipelineParams& pipeline); -void operator>>(const YAML::Node& node, - std::vector& list); -void operator>>(const YAML::Node& node, ParamManager::InferenceParams& infer); -void operator>>(const YAML::Node& node, std::vector& list); -void operator>>(const YAML::Node& node, - std::multimap& connect); -void operator>>(const YAML::Node& node, std::string& str); -void operator>>(const YAML::Node& node, bool& val); -void operator>>(const YAML::Node& node, ParamManager::CommonParams& common); - -#define YAML_PARSE(node, key, val) \ - try { \ - node[key] >> val; \ - } catch (YAML::Exception e) { \ - slog::warn << e.msg << slog::endl; \ - } catch (...) { \ +namespace Params +{ +void operator>>(const YAML::Node & node, ParamManager::PipelineParams & pipeline); +void operator>>(const YAML::Node & node, std::vector & list); +void operator>>(const YAML::Node & node, ParamManager::InferenceParams & infer); +void operator>>(const YAML::Node & node, std::vector & list); +void operator>>(const YAML::Node & node, std::multimap & connect); +void operator>>(const YAML::Node & node, std::string & str); +void operator>>(const YAML::Node & node, bool & val); +void operator>>(const YAML::Node & node, ParamManager::CommonParams & common); + +#define YAML_PARSE(node, key, val) \ + try \ + { \ + node[key] >> val; \ + } \ + catch (YAML::Exception e) \ + { \ + slog::warn << e.msg << slog::endl; \ + } \ + catch (...) \ + { \ slog::warn << "Exception occurs when parsing string." << slog::endl; \ } -void operator>>(const YAML::Node& node, - std::vector& list) { +void operator>>(const YAML::Node & node, std::vector & list) +{ slog::info << "Pipeline size: " << node.size() << slog::endl; for (unsigned i = 0; i < node.size(); i++) { ParamManager::PipelineParams temp; @@ -55,15 +56,16 @@ void operator>>(const YAML::Node& node, } } -void operator>>(const YAML::Node& node, ParamManager::CommonParams& common) { +void operator>>(const YAML::Node & node, ParamManager::CommonParams & common) +{ YAML_PARSE(node, "camera_topic", common.camera_topic) YAML_PARSE(node, "custom_cpu_library", common.custom_cpu_library) YAML_PARSE(node, "custom_cldnn_library", common.custom_cldnn_library) YAML_PARSE(node, "enable_performance_count", common.enable_performance_count) } -void operator>>(const YAML::Node& node, - ParamManager::PipelineParams& pipeline) { +void operator>>(const YAML::Node & node, ParamManager::PipelineParams & pipeline) +{ YAML_PARSE(node, "name", pipeline.name) YAML_PARSE(node, "inputs", pipeline.inputs) YAML_PARSE(node, "infers", pipeline.infers) @@ -73,8 +75,8 @@ void operator>>(const YAML::Node& node, slog::info << "Pipeline Params:name=" << pipeline.name << slog::endl; } -void operator>>(const YAML::Node& node, - std::vector& list) { +void operator>>(const YAML::Node & node, std::vector & list) +{ slog::info << "Inferences size: " << node.size() << slog::endl; for (unsigned i = 0; i < node.size(); i++) { ParamManager::InferenceParams temp_inf; @@ -83,7 +85,8 @@ void operator>>(const YAML::Node& node, } } -void operator>>(const YAML::Node& node, ParamManager::InferenceParams& infer) { +void operator>>(const YAML::Node & node, ParamManager::InferenceParams & infer) +{ YAML_PARSE(node, "name", infer.name) YAML_PARSE(node, "model", infer.model) YAML_PARSE(node, "engine", infer.engine) @@ -91,7 +94,8 @@ void operator>>(const YAML::Node& node, ParamManager::InferenceParams& infer) { slog::info << "Inference Params:name=" << infer.name << slog::endl; } -void operator>>(const YAML::Node& node, std::vector& list) { +void operator>>(const YAML::Node & node, std::vector & list) +{ for (unsigned i = 0; i < node.size(); i++) { std::string temp_i; node[i] >> temp_i; @@ -99,43 +103,48 @@ void operator>>(const YAML::Node& node, std::vector& list) { } } -void operator>>(const YAML::Node& node, - std::multimap& connect) { +void operator>>(const YAML::Node & node, std::multimap & connect) +{ for (unsigned i = 0; i < node.size(); i++) { std::string left; node[i]["left"] >> left; std::vector rights; node[i]["right"] >> rights; - for (auto& r : rights) { + for (auto & r : rights) { connect.insert({left, r}); } } } -void operator>>(const YAML::Node& node, std::string& str) { +void operator>>(const YAML::Node & node, std::string & str) +{ str = node.as(); } -void operator>>(const YAML::Node& node, bool& val) { val = node.as(); } +void operator>>(const YAML::Node & node, bool & val) +{ + val = node.as(); +} -void ParamManager::print() const { +void ParamManager::print() const +{ slog::info << "--------parameters DUMP---------------------" << slog::endl; - for (auto& pipeline : pipelines_) { + for (auto & pipeline : pipelines_) { slog::info << "Pipeline: " << pipeline.name << slog::endl; slog::info << "\tInputs: "; - for (auto& i : pipeline.inputs) { + for (auto & i : pipeline.inputs) { slog::info << i.c_str() << ", "; } slog::info << slog::endl; slog::info << "\tOutputs: "; - for (auto& i : pipeline.outputs) { + for (auto & i : pipeline.outputs) { slog::info << i.c_str() << ", "; } slog::info << slog::endl; slog::info << "\tInferences: " << slog::endl; - for (auto& infer : pipeline.infers) { + for (auto & infer : pipeline.infers) { slog::info << "\t\tName: " << infer.name << slog::endl; slog::info << "\t\tModel: " << infer.model << slog::endl; slog::info << "\t\tEngine: " << infer.engine << slog::endl; @@ -143,7 +152,7 @@ void ParamManager::print() const { } slog::info << "\tConnections: " << slog::endl; - for (auto& c : pipeline.connects) { + for (auto & c : pipeline.connects) { slog::info << "\t\t" << c.first << "->" << c.second << slog::endl; } } @@ -151,15 +160,13 @@ void ParamManager::print() const { // Pring Common Info slog::info << "Common:" << slog::endl; slog::info << "\tcamera_topic: " << common_.camera_topic << slog::endl; - slog::info << "\tcustom_cpu_library: " << common_.custom_cpu_library - << slog::endl; - slog::info << "\tcustom_cldnn_library: " << common_.custom_cldnn_library - << slog::endl; - slog::info << "\tenable_performance_count: " - << common_.enable_performance_count << slog::endl; + slog::info << "\tcustom_cpu_library: " << common_.custom_cpu_library << slog::endl; + slog::info << "\tcustom_cldnn_library: " << common_.custom_cldnn_library << slog::endl; + slog::info << "\tenable_performance_count: " << common_.enable_performance_count << slog::endl; } -void ParamManager::parse(std::string path) { +void ParamManager::parse(std::string path) +{ std::ifstream fin(path); if (fin.fail()) { slog::err << "Could not open config file:" << path << slog::endl; @@ -171,18 +178,19 @@ void ParamManager::parse(std::string path) { YAML_PARSE(doc, "Common", common_) } -std::vector ParamManager::getPipelineNames() const { +std::vector ParamManager::getPipelineNames() const +{ std::vector names; - for (auto& p : pipelines_) { + for (auto & p : pipelines_) { names.push_back(p.name); } return names; } -ParamManager::PipelineParams ParamManager::getPipeline( - const std::string& name) const { - for (auto& p : pipelines_) { +ParamManager::PipelineParams ParamManager::getPipeline(const std::string & name) const +{ + for (auto & p : pipelines_) { if (p.name == name) { return p; } From 34e9efa18f113345cf4ece301f02970bef86aed9 Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:18:43 -0500 Subject: [PATCH 03/16] code style reformat --- .../include/dynamic_vino_lib/args_helper.hpp | 78 +- .../include/dynamic_vino_lib/common.hpp | 1756 +++++++++-------- .../dynamic_vino_lib/engines/engine.hpp | 48 +- .../include/dynamic_vino_lib/factory.hpp | 43 +- .../inferences/age_gender_detection.hpp | 69 +- .../inferences/base_inference.hpp | 103 +- .../inferences/emotions_detection.hpp | 66 +- .../inferences/face_detection.hpp | 43 +- .../inferences/head_pose_detection.hpp | 77 +- .../inferences/object_detection.hpp | 67 +- .../inferences/object_segmentation.hpp | 75 +- .../dynamic_vino_lib/inputs/base_input.hpp | 86 +- .../dynamic_vino_lib/inputs/image_input.hpp | 54 +- .../inputs/realsense_camera.hpp | 47 +- .../inputs/realsense_camera_topic.hpp | 50 +- .../dynamic_vino_lib/inputs/ros2_handler.hpp | 48 +- .../inputs/standard_camera.hpp | 41 +- .../dynamic_vino_lib/inputs/video_input.hpp | 47 +- .../models/age_gender_detection_model.hpp | 57 +- .../dynamic_vino_lib/models/base_model.hpp | 62 +- .../models/emotion_detection_model.hpp | 54 +- .../models/face_detection_model.hpp | 38 +- .../models/head_pose_detection_model.hpp | 58 +- .../models/object_detection_model.hpp | 65 +- .../models/object_segmentation_model.hpp | 73 +- .../dynamic_vino_lib/outputs/base_output.hpp | 85 +- .../outputs/image_window_output.hpp | 131 +- .../outputs/ros_service_output.hpp | 58 +- .../outputs/ros_topic_output.hpp | 57 +- .../dynamic_vino_lib/outputs/rviz_output.hpp | 68 +- .../include/dynamic_vino_lib/pipeline.hpp | 58 +- .../dynamic_vino_lib/pipeline_manager.hpp | 93 +- .../dynamic_vino_lib/pipeline_params.hpp | 38 +- .../services/frame_processing_server.hpp | 78 +- .../include/dynamic_vino_lib/slog.hpp | 53 +- sample/include/utility.hpp | 174 +- .../include/vino_param_lib/param_manager.hpp | 62 +- .../include/vino_param_lib/slog.hpp | 53 +- 38 files changed, 2267 insertions(+), 1946 deletions(-) diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/args_helper.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/args_helper.hpp index b99cb995..1fb17a61 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/args_helper.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/args_helper.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with common samples functionality @@ -37,32 +35,39 @@ /** * @brief This function check input args and find images in given folder */ -void readImagesArguments(std::vector& images, - const std::string& arg) { +void readImagesArguments(std::vector& images, const std::string& arg) +{ struct stat sb; - if (stat(arg.c_str(), &sb) != 0) { - std::cout << "[ WARNING ] File " << arg << " cannot be opened!" - << std::endl; + if (stat(arg.c_str(), &sb) != 0) + { + std::cout << "[ WARNING ] File " << arg << " cannot be opened!" << std::endl; return; } - if (S_ISDIR(sb.st_mode)) { + if (S_ISDIR(sb.st_mode)) + { DIR* dp; dp = opendir(arg.c_str()); - if (dp == nullptr) { - std::cout << "[ WARNING ] Directory " << arg << " cannot be opened!" - << std::endl; + if (dp == nullptr) + { + std::cout << "[ WARNING ] Directory " << arg << " cannot be opened!" << std::endl; return; } struct dirent* ep; - while (nullptr != (ep = readdir(dp))) { + while (nullptr != (ep = readdir(dp))) + { std::string fileName = ep->d_name; - if (fileName == "." || fileName == "..") continue; - std::cout << "[ INFO ] Add file " << ep->d_name << " from directory " - << arg << "." << std::endl; + if (fileName == "." || fileName == "..") + { + continue; + } + std::cout << "[ INFO ] Add file " << ep->d_name << " from directory " << arg << "." + << std::endl; images.push_back(arg + "/" + ep->d_name); } - } else { + } + else + { images.push_back(arg); } } @@ -71,18 +76,23 @@ void readImagesArguments(std::vector& images, * @brief This function find -i/--images key in input args * It's necessary to process multiple values for single key */ -void parseImagesArguments(std::vector& images) { +void parseImagesArguments(std::vector& images) +{ std::vector args = gflags::GetArgvs(); bool readArguments = false; - for (size_t i = 0; i < args.size(); i++) { - if (args.at(i) == "-i" || args.at(i) == "--images") { + for (size_t i = 0; i < args.size(); i++) + { + if (args.at(i) == "-i" || args.at(i) == "--images") + { readArguments = true; continue; } - if (!readArguments) { + if (!readArguments) + { continue; } - if (args.at(i).c_str()[0] == '-') { + if (args.at(i).c_str()[0] == '-') + { break; } readImagesArguments(images, args.at(i)); diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/common.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/common.hpp index 1e2d542e..ff62ab86 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/common.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/common.hpp @@ -1,4 +1,3 @@ -/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,7 +11,6 @@ // 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. -*/ /** * @brief a header file with common samples functionality @@ -45,11 +43,11 @@ #include #ifndef UNUSED - #ifdef WIN32 - #define UNUSED - #else - #define UNUSED __attribute__((unused)) - #endif +#ifdef WIN32 +#define UNUSED +#else +#define UNUSED __attribute__((unused)) +#endif #endif /** @@ -57,10 +55,14 @@ * @param s - string to trim * @return trimmed string */ -inline std::string &trim(std::string &s) { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - return s; +inline std::string& trim(std::string& s) +{ + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + s.erase( + std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), + s.end()); + return s; } /** @@ -69,8 +71,9 @@ inline std::string &trim(std::string &s) { * @return TargetDevice value that corresponds to input string. * eDefault in case no corresponding value was found */ -static InferenceEngine::TargetDevice getDeviceFromStr(const std::string &deviceName) { - return InferenceEngine::TargetDeviceInfo::fromStr(deviceName); +static InferenceEngine::TargetDevice getDeviceFromStr(const std::string& deviceName) +{ + return InferenceEngine::TargetDeviceInfo::fromStr(deviceName); } /** @@ -80,16 +83,20 @@ static InferenceEngine::TargetDevice getDeviceFromStr(const std::string &deviceN * @param device - device to infer on * @return Plugin pointer */ -static InferenceEngine::InferenceEnginePluginPtr selectPlugin(const std::vector &pluginDirs, - const std::string &plugin, - InferenceEngine::TargetDevice device) { - InferenceEngine::PluginDispatcher dispatcher(pluginDirs); - - if (!plugin.empty()) { - return dispatcher.getPluginByName(plugin); - } else { - return dispatcher.getSuitablePlugin(device); - } +static InferenceEngine::InferenceEnginePluginPtr +selectPlugin(const std::vector& pluginDirs, const std::string& plugin, + InferenceEngine::TargetDevice device) +{ + InferenceEngine::PluginDispatcher dispatcher(pluginDirs); + + if (!plugin.empty()) + { + return dispatcher.getPluginByName(plugin); + } + else + { + return dispatcher.getSuitablePlugin(device); + } } /** @@ -99,10 +106,11 @@ static InferenceEngine::InferenceEnginePluginPtr selectPlugin(const std::vector< * @param device - string representation of device to infer on * @return Plugin pointer */ -static UNUSED InferenceEngine::InferenceEnginePluginPtr selectPlugin(const std::vector &pluginDirs, - const std::string &plugin, - const std::string &device) { - return selectPlugin(pluginDirs, plugin, getDeviceFromStr(device)); +static UNUSED InferenceEngine::InferenceEnginePluginPtr +selectPlugin(const std::vector& pluginDirs, const std::string& plugin, + const std::string& device) +{ + return selectPlugin(pluginDirs, plugin, getDeviceFromStr(device)); } /** @@ -110,10 +118,14 @@ static UNUSED InferenceEngine::InferenceEnginePluginPtr selectPlugin(const std:: * @param filepath - full file name * @return filename without extension */ -static UNUSED std::string fileNameNoExt(const std::string &filepath) { - auto pos = filepath.rfind('.'); - if (pos == std::string::npos) return filepath; - return filepath.substr(0, pos); +static UNUSED std::string fileNameNoExt(const std::string& filepath) +{ + auto pos = filepath.rfind('.'); + if (pos == std::string::npos) + { + return filepath; + } + return filepath.substr(0, pos); } /** @@ -121,145 +133,189 @@ static UNUSED std::string fileNameNoExt(const std::string &filepath) { * @param filename - name of the file which extension should be extracted * @return string with extracted file extension */ -inline std::string fileExt(const std::string& filename) { - auto pos = filename.rfind('.'); - if (pos == std::string::npos) return ""; - return filename.substr(pos + 1); +inline std::string fileExt(const std::string& filename) +{ + auto pos = filename.rfind('.'); + if (pos == std::string::npos) + { + return ""; + } + return filename.substr(pos + 1); } -static UNUSED std::ostream &operator<<(std::ostream &os, const InferenceEngine::Version *version) { - os << "\n\tAPI version ............ "; - if (nullptr == version) { - os << "UNKNOWN"; - } else { - os << version->apiVersion.major << "." << version->apiVersion.minor; - if (nullptr != version->buildNumber) { - os << "\n\t" << "Build .................. " << version->buildNumber; - } - if (nullptr != version->description) { - os << "\n\t" << "Description ....... " << version->description; - } +static UNUSED std::ostream& operator<<(std::ostream& os, const InferenceEngine::Version* version) +{ + os << "\n\tAPI version ............ "; + if (nullptr == version) + { + os << "UNKNOWN"; + } + else + { + os << version->apiVersion.major << "." << version->apiVersion.minor; + if (nullptr != version->buildNumber) + { + os << "\n\t" + << "Build .................. " << version->buildNumber; } - return os; + if (nullptr != version->description) + { + os << "\n\t" + << "Description ....... " << version->description; + } + } + return os; } /** * @class PluginVersion * @brief A PluginVersion class stores plugin version and initialization status */ -struct PluginVersion : public InferenceEngine::Version { - bool initialized = false; - - explicit PluginVersion(const InferenceEngine::Version *ver) { - if (nullptr == ver) { - return; - } - InferenceEngine::Version::operator=(*ver); - initialized = true; - } - - operator bool() const noexcept { - return initialized; +struct PluginVersion : public InferenceEngine::Version +{ + bool initialized = false; + + explicit PluginVersion(const InferenceEngine::Version* ver) + { + if (nullptr == ver) + { + return; } + InferenceEngine::Version::operator=(*ver); + initialized = true; + } + + operator bool() const noexcept + { + return initialized; + } }; -static UNUSED std::ostream &operator<<(std::ostream &os, const PluginVersion &version) { - os << "\tPlugin version ......... "; - if (!version) { - os << "UNKNOWN"; - } else { - os << version.apiVersion.major << "." << version.apiVersion.minor; - } - - os << "\n\tPlugin name ............ "; - if (!version || version.description == nullptr) { - os << "UNKNOWN"; - } else { - os << version.description; - } - - os << "\n\tPlugin build ........... "; - if (!version || version.buildNumber == nullptr) { - os << "UNKNOWN"; - } else { - os << version.buildNumber; - } - - return os; +static UNUSED std::ostream& operator<<(std::ostream& os, const PluginVersion& version) +{ + os << "\tPlugin version ......... "; + if (!version) + { + os << "UNKNOWN"; + } + else + { + os << version.apiVersion.major << "." << version.apiVersion.minor; + } + + os << "\n\tPlugin name ............ "; + if (!version || version.description == nullptr) + { + os << "UNKNOWN"; + } + else + { + os << version.description; + } + + os << "\n\tPlugin build ........... "; + if (!version || version.buildNumber == nullptr) + { + os << "UNKNOWN"; + } + else + { + os << version.buildNumber; + } + + return os; } -inline void printPluginVersion(InferenceEngine::InferenceEnginePluginPtr ptr, std::ostream& stream) { - const PluginVersion *pluginVersion; - ptr->GetVersion((const InferenceEngine::Version*&)pluginVersion); - stream << pluginVersion << std::endl; +inline void printPluginVersion(InferenceEngine::InferenceEnginePluginPtr ptr, std::ostream& stream) +{ + const PluginVersion* pluginVersion; + ptr->GetVersion((const InferenceEngine::Version*&)pluginVersion); + stream << pluginVersion << std::endl; } -static UNUSED std::vector> blobToImageOutputArray(InferenceEngine::TBlob::Ptr output, - size_t *pWidth, size_t *pHeight, - size_t *pChannels) { - std::vector> outArray; - size_t W = output->dims().at(0); - size_t H = output->dims().at(1); - size_t C = output->dims().at(2); - - // Get classes - const float *outData = output->data(); - for (unsigned h = 0; h < H; h++) { - std::vector row; - for (unsigned w = 0; w < W; w++) { - float max_value = outData[h * W + w]; - size_t index = 0; - for (size_t c = 1; c < C; c++) { - size_t dataIndex = c * H * W + h * W + w; - if (outData[dataIndex] > max_value) { - index = c; - max_value = outData[dataIndex]; - } - } - row.push_back(index); +static UNUSED std::vector> blobToImageOutputArray( + InferenceEngine::TBlob::Ptr output, size_t* pWidth, size_t* pHeight, size_t* pChannels) +{ + std::vector> outArray; + size_t W = output->dims().at(0); + size_t H = output->dims().at(1); + size_t C = output->dims().at(2); + + // Get classes + const float* outData = output->data(); + for (unsigned h = 0; h < H; h++) + { + std::vector row; + for (unsigned w = 0; w < W; w++) + { + float max_value = outData[h * W + w]; + size_t index = 0; + for (size_t c = 1; c < C; c++) + { + size_t dataIndex = c * H * W + h * W + w; + if (outData[dataIndex] > max_value) + { + index = c; + max_value = outData[dataIndex]; } - outArray.push_back(row); + } + row.push_back(index); } - - if (pWidth != nullptr) *pWidth = W; - if (pHeight != nullptr) *pHeight = H; - if (pChannels != nullptr) *pChannels = C; - - return outArray; + outArray.push_back(row); + } + + if (pWidth != nullptr) + { + *pWidth = W; + } + if (pHeight != nullptr) + { + *pHeight = H; + } + if (pChannels != nullptr) + { + *pChannels = C; + } + + return outArray; } /** * @class Color * @brief A Color class stores channels of a given color */ -class Color { +class Color +{ private: - unsigned char _r; - unsigned char _g; - unsigned char _b; + unsigned char _r; + unsigned char _g; + unsigned char _b; public: - /** - * A default constructor. - * @param r - value for red channel - * @param g - value for green channel - * @param b - value for blue channel - */ - Color(unsigned char r, - unsigned char g, - unsigned char b) : _r(r), _g(g), _b(b) {} - - inline unsigned char red() { - return _r; - } - - inline unsigned char blue() { - return _b; - } - - inline unsigned char green() { - return _g; - } + /** + * A default constructor. + * @param r - value for red channel + * @param g - value for green channel + * @param b - value for blue channel + */ + Color(unsigned char r, unsigned char g, unsigned char b) : _r(r), _g(g), _b(b) + { + } + + inline unsigned char red() + { + return _r; + } + + inline unsigned char blue() + { + return _b; + } + + inline unsigned char green() + { + return _g; + } }; // TODO : keep only one version of writeOutputBMP @@ -271,109 +327,100 @@ class Color { * @param classesNum - the number of classes * @return false if error else true */ -static UNUSED void writeOutputBmp(std::vector> data, size_t classesNum, std::ostream &outFile) { - unsigned int seed = (unsigned int) time(NULL); - // Known colors for training classes from Cityscape dataset - static std::vector colors = { - {128, 64, 128}, - {232, 35, 244}, - {70, 70, 70}, - {156, 102, 102}, - {153, 153, 190}, - {153, 153, 153}, - {30, 170, 250}, - {0, 220, 220}, - {35, 142, 107}, - {152, 251, 152}, - {180, 130, 70}, - {60, 20, 220}, - {0, 0, 255}, - {142, 0, 0}, - {70, 0, 0}, - {100, 60, 0}, - {90, 0, 0}, - {230, 0, 0}, - {32, 11, 119}, - {0, 74, 111}, - {81, 0, 81} - }; - - while (classesNum > colors.size()) { - static std::mt19937 rng(seed); - std::uniform_int_distribution dist(0, 255); - Color color(dist(rng), dist(rng), dist(rng)); - colors.push_back(color); - } - - unsigned char file[14] = { - 'B', 'M', // magic - 0, 0, 0, 0, // size in bytes - 0, 0, // app data - 0, 0, // app data - 40 + 14, 0, 0, 0 // start of data offset - }; - unsigned char info[40] = { - 40, 0, 0, 0, // info hd size - 0, 0, 0, 0, // width - 0, 0, 0, 0, // height - 1, 0, // number color planes - 24, 0, // bits per pixel - 0, 0, 0, 0, // compression is none - 0, 0, 0, 0, // image bits size - 0x13, 0x0B, 0, 0, // horz resolution in pixel / m - 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) - 0, 0, 0, 0, // #colors in palette - 0, 0, 0, 0, // #important colors - }; - - auto height = data.size(); - auto width = data.at(0).size(); - - if (height > (size_t) std::numeric_limits::max || width > (size_t) std::numeric_limits::max) { - THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; - } - - int padSize = static_cast(4 - (width * 3) % 4) % 4; - int sizeData = static_cast(width * height * 3 + height * padSize); - int sizeAll = sizeData + sizeof(file) + sizeof(info); - - file[2] = (unsigned char) (sizeAll); - file[3] = (unsigned char) (sizeAll >> 8); - file[4] = (unsigned char) (sizeAll >> 16); - file[5] = (unsigned char) (sizeAll >> 24); - - info[4] = (unsigned char) (width); - info[5] = (unsigned char) (width >> 8); - info[6] = (unsigned char) (width >> 16); - info[7] = (unsigned char) (width >> 24); - - int32_t negativeHeight = -(int32_t) height; - info[8] = (unsigned char) (negativeHeight); - info[9] = (unsigned char) (negativeHeight >> 8); - info[10] = (unsigned char) (negativeHeight >> 16); - info[11] = (unsigned char) (negativeHeight >> 24); - - info[20] = (unsigned char) (sizeData); - info[21] = (unsigned char) (sizeData >> 8); - info[22] = (unsigned char) (sizeData >> 16); - info[23] = (unsigned char) (sizeData >> 24); - - outFile.write(reinterpret_cast(file), sizeof(file)); - outFile.write(reinterpret_cast(info), sizeof(info)); - - unsigned char pad[3] = {0, 0, 0}; - - for (size_t y = 0; y < height; y++) { - for (size_t x = 0; x < width; x++) { - unsigned char pixel[3]; - size_t index = data.at(y).at(x); - pixel[0] = colors.at(index).red(); - pixel[1] = colors.at(index).green(); - pixel[2] = colors.at(index).blue(); - outFile.write(reinterpret_cast(pixel), 3); - } - outFile.write(reinterpret_cast(pad), padSize); +static UNUSED void writeOutputBmp(std::vector> data, size_t classesNum, + std::ostream& outFile) +{ + unsigned int seed = (unsigned int)time(NULL); + // Known colors for training classes from Cityscape dataset + static std::vector colors = { { 128, 64, 128 }, { 232, 35, 244 }, { 70, 70, 70 }, + { 156, 102, 102 }, { 153, 153, 190 }, { 153, 153, 153 }, + { 30, 170, 250 }, { 0, 220, 220 }, { 35, 142, 107 }, + { 152, 251, 152 }, { 180, 130, 70 }, { 60, 20, 220 }, + { 0, 0, 255 }, { 142, 0, 0 }, { 70, 0, 0 }, + { 100, 60, 0 }, { 90, 0, 0 }, { 230, 0, 0 }, + { 32, 11, 119 }, { 0, 74, 111 }, { 81, 0, 81 } }; + + while (classesNum > colors.size()) + { + static std::mt19937 rng(seed); + std::uniform_int_distribution dist(0, 255); + Color color(dist(rng), dist(rng), dist(rng)); + colors.push_back(color); + } + + unsigned char file[14] = { + 'B', 'M', // magic + 0, 0, 0, 0, // size in bytes + 0, 0, // app data + 0, 0, // app data + 40 + 14, 0, 0, 0 // start of data offset + }; + unsigned char info[40] = { + 40, 0, 0, 0, // info hd size + 0, 0, 0, 0, // width + 0, 0, 0, 0, // height + 1, 0, // number color planes + 24, 0, // bits per pixel + 0, 0, 0, 0, // compression is none + 0, 0, 0, 0, // image bits size + 0x13, 0x0B, 0, 0, // horz resolution in pixel / m + 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) + 0, 0, 0, 0, // #colors in palette + 0, 0, 0, 0, // #important colors + }; + + auto height = data.size(); + auto width = data.at(0).size(); + + if (height > (size_t)std::numeric_limits::max || + width > (size_t)std::numeric_limits::max) + { + THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; + } + + int padSize = static_cast(4 - (width * 3) % 4) % 4; + int sizeData = static_cast(width * height * 3 + height * padSize); + int sizeAll = sizeData + sizeof(file) + sizeof(info); + + file[2] = (unsigned char)(sizeAll); + file[3] = (unsigned char)(sizeAll >> 8); + file[4] = (unsigned char)(sizeAll >> 16); + file[5] = (unsigned char)(sizeAll >> 24); + + info[4] = (unsigned char)(width); + info[5] = (unsigned char)(width >> 8); + info[6] = (unsigned char)(width >> 16); + info[7] = (unsigned char)(width >> 24); + + int32_t negativeHeight = -(int32_t)height; + info[8] = (unsigned char)(negativeHeight); + info[9] = (unsigned char)(negativeHeight >> 8); + info[10] = (unsigned char)(negativeHeight >> 16); + info[11] = (unsigned char)(negativeHeight >> 24); + + info[20] = (unsigned char)(sizeData); + info[21] = (unsigned char)(sizeData >> 8); + info[22] = (unsigned char)(sizeData >> 16); + info[23] = (unsigned char)(sizeData >> 24); + + outFile.write(reinterpret_cast(file), sizeof(file)); + outFile.write(reinterpret_cast(info), sizeof(info)); + + unsigned char pad[3] = { 0, 0, 0 }; + + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + unsigned char pixel[3]; + size_t index = data.at(y).at(x); + pixel[0] = colors.at(index).red(); + pixel[1] = colors.at(index).green(); + pixel[2] = colors.at(index).blue(); + outFile.write(reinterpret_cast(pixel), 3); } + outFile.write(reinterpret_cast(pad), padSize); + } } /** @@ -384,83 +431,89 @@ static UNUSED void writeOutputBmp(std::vector> data, size_t * @param width - width of the target image * @return false if error else true */ -static UNUSED bool writeOutputBmp(std::string name, unsigned char *data, size_t height, size_t width) { - std::ofstream outFile; - outFile.open(name, std::ofstream::binary); - if (!outFile.is_open()) { - return false; - } - - unsigned char file[14] = { - 'B', 'M', // magic - 0, 0, 0, 0, // size in bytes - 0, 0, // app data - 0, 0, // app data - 40 + 14, 0, 0, 0 // start of data offset - }; - unsigned char info[40] = { - 40, 0, 0, 0, // info hd size - 0, 0, 0, 0, // width - 0, 0, 0, 0, // height - 1, 0, // number color planes - 24, 0, // bits per pixel - 0, 0, 0, 0, // compression is none - 0, 0, 0, 0, // image bits size - 0x13, 0x0B, 0, 0, // horz resolution in pixel / m - 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) - 0, 0, 0, 0, // #colors in palette - 0, 0, 0, 0, // #important colors - }; - - if (height > (size_t)std::numeric_limits::max || width > (size_t)std::numeric_limits::max) { - THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; +static UNUSED bool writeOutputBmp(std::string name, unsigned char* data, size_t height, + size_t width) +{ + std::ofstream outFile; + outFile.open(name, std::ofstream::binary); + if (!outFile.is_open()) + { + return false; + } + + unsigned char file[14] = { + 'B', 'M', // magic + 0, 0, 0, 0, // size in bytes + 0, 0, // app data + 0, 0, // app data + 40 + 14, 0, 0, 0 // start of data offset + }; + unsigned char info[40] = { + 40, 0, 0, 0, // info hd size + 0, 0, 0, 0, // width + 0, 0, 0, 0, // height + 1, 0, // number color planes + 24, 0, // bits per pixel + 0, 0, 0, 0, // compression is none + 0, 0, 0, 0, // image bits size + 0x13, 0x0B, 0, 0, // horz resolution in pixel / m + 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) + 0, 0, 0, 0, // #colors in palette + 0, 0, 0, 0, // #important colors + }; + + if (height > (size_t)std::numeric_limits::max || + width > (size_t)std::numeric_limits::max) + { + THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; + } + + int padSize = static_cast(4 - (width * 3) % 4) % 4; + int sizeData = static_cast(width * height * 3 + height * padSize); + int sizeAll = sizeData + sizeof(file) + sizeof(info); + + file[2] = (unsigned char)(sizeAll); + file[3] = (unsigned char)(sizeAll >> 8); + file[4] = (unsigned char)(sizeAll >> 16); + file[5] = (unsigned char)(sizeAll >> 24); + + info[4] = (unsigned char)(width); + info[5] = (unsigned char)(width >> 8); + info[6] = (unsigned char)(width >> 16); + info[7] = (unsigned char)(width >> 24); + + int32_t negativeHeight = -(int32_t)height; + info[8] = (unsigned char)(negativeHeight); + info[9] = (unsigned char)(negativeHeight >> 8); + info[10] = (unsigned char)(negativeHeight >> 16); + info[11] = (unsigned char)(negativeHeight >> 24); + + info[20] = (unsigned char)(sizeData); + info[21] = (unsigned char)(sizeData >> 8); + info[22] = (unsigned char)(sizeData >> 16); + info[23] = (unsigned char)(sizeData >> 24); + + outFile.write(reinterpret_cast(file), sizeof(file)); + outFile.write(reinterpret_cast(info), sizeof(info)); + + unsigned char pad[3] = { 0, 0, 0 }; + + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + unsigned char pixel[3]; + pixel[0] = data[y * width * 3 + x * 3]; + pixel[1] = data[y * width * 3 + x * 3 + 1]; + pixel[2] = data[y * width * 3 + x * 3 + 2]; + + outFile.write(reinterpret_cast(pixel), 3); } - - int padSize = static_cast(4 - (width * 3) % 4) % 4; - int sizeData = static_cast(width * height * 3 + height * padSize); - int sizeAll = sizeData + sizeof(file) + sizeof(info); - - file[2] = (unsigned char)(sizeAll); - file[3] = (unsigned char)(sizeAll >> 8); - file[4] = (unsigned char)(sizeAll >> 16); - file[5] = (unsigned char)(sizeAll >> 24); - - info[4] = (unsigned char)(width); - info[5] = (unsigned char)(width >> 8); - info[6] = (unsigned char)(width >> 16); - info[7] = (unsigned char)(width >> 24); - - int32_t negativeHeight = -(int32_t)height; - info[8] = (unsigned char)(negativeHeight); - info[9] = (unsigned char)(negativeHeight >> 8); - info[10] = (unsigned char)(negativeHeight >> 16); - info[11] = (unsigned char)(negativeHeight >> 24); - - info[20] = (unsigned char)(sizeData); - info[21] = (unsigned char)(sizeData >> 8); - info[22] = (unsigned char)(sizeData >> 16); - info[23] = (unsigned char)(sizeData >> 24); - - outFile.write(reinterpret_cast(file), sizeof(file)); - outFile.write(reinterpret_cast(info), sizeof(info)); - - unsigned char pad[3] = { 0, 0, 0 }; - - for (size_t y = 0; y < height; y++) { - for (size_t x = 0; x < width; x++) { - unsigned char pixel[3]; - pixel[0] = data[y * width * 3 + x * 3]; - pixel[1] = data[y * width * 3 + x * 3 + 1]; - pixel[2] = data[y * width * 3 + x * 3 + 2]; - - outFile.write(reinterpret_cast(pixel), 3); - } - outFile.write(reinterpret_cast(pad), padSize); - } - return true; + outFile.write(reinterpret_cast(pad), padSize); + } + return true; } - /** * @brief Adds colored rectangles to the image * @param data - data where rectangles are put @@ -469,77 +522,91 @@ static UNUSED bool writeOutputBmp(std::string name, unsigned char *data, size_t * @param rectangles - vector points for the rectangle, should be 4x compared to num classes * @param classes - vector of classes */ -static UNUSED void addRectangles(unsigned char *data, size_t height, size_t width, std::vector rectangles, std::vector classes) { - std::vector colors = { - { 128, 64, 128 }, - { 232, 35, 244 }, - { 70, 70, 70 }, - { 156, 102, 102 }, - { 153, 153, 190 }, - { 153, 153, 153 }, - { 30, 170, 250 }, - { 0, 220, 220 }, - { 35, 142, 107 }, - { 152, 251, 152 }, - { 180, 130, 70 }, - { 60, 20, 220 }, - { 0, 0, 255 }, - { 142, 0, 0 }, - { 70, 0, 0 }, - { 100, 60, 0 }, - { 90, 0, 0 }, - { 230, 0, 0 }, - { 32, 11, 119 }, - { 0, 74, 111 }, - { 81, 0, 81 } - }; - if (rectangles.size() % 4 != 0 || rectangles.size() / 4 != classes.size()) { - return; +static UNUSED void addRectangles(unsigned char* data, size_t height, size_t width, + std::vector rectangles, std::vector classes) +{ + std::vector colors = { { 128, 64, 128 }, { 232, 35, 244 }, { 70, 70, 70 }, + { 156, 102, 102 }, { 153, 153, 190 }, { 153, 153, 153 }, + { 30, 170, 250 }, { 0, 220, 220 }, { 35, 142, 107 }, + { 152, 251, 152 }, { 180, 130, 70 }, { 60, 20, 220 }, + { 0, 0, 255 }, { 142, 0, 0 }, { 70, 0, 0 }, + { 100, 60, 0 }, { 90, 0, 0 }, { 230, 0, 0 }, + { 32, 11, 119 }, { 0, 74, 111 }, { 81, 0, 81 } }; + if (rectangles.size() % 4 != 0 || rectangles.size() / 4 != classes.size()) + { + return; + } + + for (size_t i = 0; i < classes.size(); i++) + { + int x = rectangles.at(i * 4); + int y = rectangles.at(i * 4 + 1); + int w = rectangles.at(i * 4 + 2); + int h = rectangles.at(i * 4 + 3); + + if (x < 0) + { + x = 0; + } + if (y < 0) + { + y = 0; + } + if (w < 0) + { + w = 0; + } + if (h < 0) + { + h = 0; } - for (size_t i = 0; i < classes.size(); i++) { - int x = rectangles.at(i * 4); - int y = rectangles.at(i * 4 + 1); - int w = rectangles.at(i * 4 + 2); - int h = rectangles.at(i * 4 + 3); - - if (x < 0) x = 0; - if (y < 0) y = 0; - if (w < 0) w = 0; - if (h < 0) h = 0; - - if (x >= width) { x = width - 1; w = 0; } - if (y >= height) { y = height - 1; h = 0; } - - if (x + w >= width) { w = width - x - 1; } - if (y + h >= height) { h = height - y - 1; } - - size_t shift_first = y*width * 3; - size_t shift_second = (y + h)*width * 3; - int cls = classes.at(i) % colors.size(); - for (int i = x; i < x + w; i++) { - data[shift_first + i * 3] = colors.at(cls).red(); - data[shift_first + i * 3 + 1] = colors.at(cls).green(); - data[shift_first + i * 3 + 2] = colors.at(cls).blue(); - data[shift_second + i * 3] = colors.at(cls).red(); - data[shift_second + i * 3 + 1] = colors.at(cls).green(); - data[shift_second + i * 3 + 2] = colors.at(cls).blue(); - } + if (x >= width) + { + x = width - 1; + w = 0; + } + if (y >= height) + { + y = height - 1; + h = 0; + } - shift_first = x * 3; - shift_second = (x + w) * 3; - for (int i = y; i < y + h; i++) { - data[shift_first + i*width * 3] = colors.at(cls).red(); - data[shift_first + i*width * 3 + 1] = colors.at(cls).green(); - data[shift_first + i*width * 3 + 2] = colors.at(cls).blue(); - data[shift_second + i*width * 3] = colors.at(cls).red(); - data[shift_second + i*width * 3 + 1] = colors.at(cls).green(); - data[shift_second + i*width * 3 + 2] = colors.at(cls).blue(); - } + if (x + w >= width) + { + w = width - x - 1; + } + if (y + h >= height) + { + h = height - y - 1; } -} + size_t shift_first = y * width * 3; + size_t shift_second = (y + h) * width * 3; + int cls = classes.at(i) % colors.size(); + for (int i = x; i < x + w; i++) + { + data[shift_first + i * 3] = colors.at(cls).red(); + data[shift_first + i * 3 + 1] = colors.at(cls).green(); + data[shift_first + i * 3 + 2] = colors.at(cls).blue(); + data[shift_second + i * 3] = colors.at(cls).red(); + data[shift_second + i * 3 + 1] = colors.at(cls).green(); + data[shift_second + i * 3 + 2] = colors.at(cls).blue(); + } + shift_first = x * 3; + shift_second = (x + w) * 3; + for (int i = y; i < y + h; i++) + { + data[shift_first + i * width * 3] = colors.at(cls).red(); + data[shift_first + i * width * 3 + 1] = colors.at(cls).green(); + data[shift_first + i * width * 3 + 2] = colors.at(cls).blue(); + data[shift_second + i * width * 3] = colors.at(cls).red(); + data[shift_second + i * width * 3 + 1] = colors.at(cls).green(); + data[shift_second + i * width * 3 + 2] = colors.at(cls).blue(); + } + } +} /** * Write output data to image @@ -549,428 +616,530 @@ static UNUSED void addRectangles(unsigned char *data, size_t height, size_t widt * \return false if error else true */ -static UNUSED bool writeOutputBmp(unsigned char *data, size_t height, size_t width, std::ostream &outFile) { - unsigned char file[14] = { - 'B', 'M', // magic - 0, 0, 0, 0, // size in bytes - 0, 0, // app data - 0, 0, // app data - 40+14, 0, 0, 0 // start of data offset - }; - unsigned char info[40] = { - 40, 0, 0, 0, // info hd size - 0, 0, 0, 0, // width - 0, 0, 0, 0, // height - 1, 0, // number color planes - 24, 0, // bits per pixel - 0, 0, 0, 0, // compression is none - 0, 0, 0, 0, // image bits size - 0x13, 0x0B, 0, 0, // horz resolution in pixel / m - 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) - 0, 0, 0, 0, // #colors in palette - 0, 0, 0, 0, // #important colors - }; - - if (height > (size_t)std::numeric_limits::max || width > (size_t)std::numeric_limits::max) { - THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; - } - - int padSize = static_cast(4 - (width * 3) % 4) % 4; - int sizeData = static_cast(width * height * 3 + height * padSize); - int sizeAll = sizeData + sizeof(file) + sizeof(info); - - file[ 2] = (unsigned char)(sizeAll ); - file[ 3] = (unsigned char)(sizeAll >> 8); - file[ 4] = (unsigned char)(sizeAll >> 16); - file[ 5] = (unsigned char)(sizeAll >> 24); - - info[ 4] = (unsigned char)(width ); - info[ 5] = (unsigned char)(width >> 8); - info[ 6] = (unsigned char)(width >> 16); - info[ 7] = (unsigned char)(width >> 24); - - int32_t negativeHeight = -(int32_t)height; - info[ 8] = (unsigned char)(negativeHeight ); - info[ 9] = (unsigned char)(negativeHeight >> 8); - info[10] = (unsigned char)(negativeHeight >> 16); - info[11] = (unsigned char)(negativeHeight >> 24); - - info[20] = (unsigned char)(sizeData ); - info[21] = (unsigned char)(sizeData >> 8); - info[22] = (unsigned char)(sizeData >> 16); - info[23] = (unsigned char)(sizeData >> 24); - - outFile.write(reinterpret_cast(file), sizeof(file)); - outFile.write(reinterpret_cast(info), sizeof(info)); - - unsigned char pad[3] = {0, 0, 0}; - - for (size_t y = 0; y < height; y++) { - for (size_t x = 0; x < width; x++) { - unsigned char pixel[3]; - pixel[0] = data[y*width*3 + x*3]; - pixel[1] = data[y*width*3 + x*3 + 1]; - pixel[2] = data[y*width*3 + x*3 + 2]; - outFile.write(reinterpret_cast(pixel), 3); - } - outFile.write(reinterpret_cast(pad), padSize); +static UNUSED bool writeOutputBmp(unsigned char* data, size_t height, size_t width, + std::ostream& outFile) +{ + unsigned char file[14] = { + 'B', 'M', // magic + 0, 0, 0, 0, // size in bytes + 0, 0, // app data + 0, 0, // app data + 40 + 14, 0, 0, 0 // start of data offset + }; + unsigned char info[40] = { + 40, 0, 0, 0, // info hd size + 0, 0, 0, 0, // width + 0, 0, 0, 0, // height + 1, 0, // number color planes + 24, 0, // bits per pixel + 0, 0, 0, 0, // compression is none + 0, 0, 0, 0, // image bits size + 0x13, 0x0B, 0, 0, // horz resolution in pixel / m + 0x13, 0x0B, 0, 0, // vert resolution (0x03C3 = 96 dpi, 0x0B13 = 72 dpi) + 0, 0, 0, 0, // #colors in palette + 0, 0, 0, 0, // #important colors + }; + + if (height > (size_t)std::numeric_limits::max || + width > (size_t)std::numeric_limits::max) + { + THROW_IE_EXCEPTION << "File size is too big: " << height << " X " << width; + } + + int padSize = static_cast(4 - (width * 3) % 4) % 4; + int sizeData = static_cast(width * height * 3 + height * padSize); + int sizeAll = sizeData + sizeof(file) + sizeof(info); + + file[2] = (unsigned char)(sizeAll); + file[3] = (unsigned char)(sizeAll >> 8); + file[4] = (unsigned char)(sizeAll >> 16); + file[5] = (unsigned char)(sizeAll >> 24); + + info[4] = (unsigned char)(width); + info[5] = (unsigned char)(width >> 8); + info[6] = (unsigned char)(width >> 16); + info[7] = (unsigned char)(width >> 24); + + int32_t negativeHeight = -(int32_t)height; + info[8] = (unsigned char)(negativeHeight); + info[9] = (unsigned char)(negativeHeight >> 8); + info[10] = (unsigned char)(negativeHeight >> 16); + info[11] = (unsigned char)(negativeHeight >> 24); + + info[20] = (unsigned char)(sizeData); + info[21] = (unsigned char)(sizeData >> 8); + info[22] = (unsigned char)(sizeData >> 16); + info[23] = (unsigned char)(sizeData >> 24); + + outFile.write(reinterpret_cast(file), sizeof(file)); + outFile.write(reinterpret_cast(info), sizeof(info)); + + unsigned char pad[3] = { 0, 0, 0 }; + + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < width; x++) + { + unsigned char pixel[3]; + pixel[0] = data[y * width * 3 + x * 3]; + pixel[1] = data[y * width * 3 + x * 3 + 1]; + pixel[2] = data[y * width * 3 + x * 3 + 2]; + outFile.write(reinterpret_cast(pixel), 3); } + outFile.write(reinterpret_cast(pad), padSize); + } - return true; + return true; } -inline double getDurationOf(std::function func) { - auto t0 = std::chrono::high_resolution_clock::now(); - func(); - auto t1 = std::chrono::high_resolution_clock::now(); - std::chrono::duration fs = t1 - t0; - return std::chrono::duration_cast>>(fs).count(); +inline double getDurationOf(std::function func) +{ + auto t0 = std::chrono::high_resolution_clock::now(); + func(); + auto t1 = std::chrono::high_resolution_clock::now(); + std::chrono::duration fs = t1 - t0; + return std::chrono::duration_cast>>(fs).count(); } - -static UNUSED void printPerformanceCounts(const std::map& performanceMap, - std::ostream &stream, - bool bshowHeader = true) { - long long totalTime = 0; - // Print performance counts - if (bshowHeader) { - stream << std::endl << "performance counts:" << std::endl << std::endl; +static UNUSED void printPerformanceCounts( + const std::map& performanceMap, + std::ostream& stream, bool bshowHeader = true) +{ + long long totalTime = 0; + // Print performance counts + if (bshowHeader) + { + stream << std::endl << "performance counts:" << std::endl << std::endl; + } + for (const auto& it : performanceMap) + { + std::string toPrint(it.first); + const int maxLayerName = 30; + + if (it.first.length() >= maxLayerName) + { + toPrint = it.first.substr(0, maxLayerName - 4); + toPrint += "..."; } - for (const auto & it : performanceMap) { - std::string toPrint(it.first); - const int maxLayerName = 30; - - if (it.first.length() >= maxLayerName) { - toPrint = it.first.substr(0, maxLayerName - 4); - toPrint += "..."; - } - - stream << std::setw(maxLayerName) << std::left << toPrint; - switch (it.second.status) { - case InferenceEngine::InferenceEngineProfileInfo::EXECUTED: - stream << std::setw(15) << std::left << "EXECUTED"; - break; - case InferenceEngine::InferenceEngineProfileInfo::NOT_RUN: - stream << std::setw(15) << std::left << "NOT_RUN"; - break; - case InferenceEngine::InferenceEngineProfileInfo::OPTIMIZED_OUT: - stream << std::setw(15) << std::left << "OPTIMIZED_OUT"; - break; - } - stream << std::setw(30) << std::left << "layerType: " + std::string(it.second.layer_type) + " "; - stream << std::setw(20) << std::left << "realTime: " + std::to_string(it.second.realTime_uSec); - stream << std::setw(20) << std::left << " cpu: " + std::to_string(it.second.cpu_uSec); - stream << " execType: " << it.second.exec_type << std::endl; - if (it.second.realTime_uSec > 0) { - totalTime += it.second.realTime_uSec; - } + stream << std::setw(maxLayerName) << std::left << toPrint; + switch (it.second.status) + { + case InferenceEngine::InferenceEngineProfileInfo::EXECUTED: + stream << std::setw(15) << std::left << "EXECUTED"; + break; + case InferenceEngine::InferenceEngineProfileInfo::NOT_RUN: + stream << std::setw(15) << std::left << "NOT_RUN"; + break; + case InferenceEngine::InferenceEngineProfileInfo::OPTIMIZED_OUT: + stream << std::setw(15) << std::left << "OPTIMIZED_OUT"; + break; } - stream << std::setw(20) << std::left << "Total time: " + std::to_string(totalTime) << " microseconds" << std::endl; + stream << std::setw(30) << std::left << "layerType: " + std::string(it.second.layer_type) + " "; + stream << std::setw(20) << std::left << "realTime: " + std::to_string(it.second.realTime_uSec); + stream << std::setw(20) << std::left << " cpu: " + std::to_string(it.second.cpu_uSec); + stream << " execType: " << it.second.exec_type << std::endl; + if (it.second.realTime_uSec > 0) + { + totalTime += it.second.realTime_uSec; + } + } + stream << std::setw(20) << std::left << "Total time: " + std::to_string(totalTime) + << " microseconds" << std::endl; } -static UNUSED void printPerformanceCounts(InferenceEngine::InferRequest request, std::ostream &stream) { - auto perfomanceMap = request.GetPerformanceCounts(); - printPerformanceCounts(perfomanceMap, stream); +static UNUSED void printPerformanceCounts(InferenceEngine::InferRequest request, + std::ostream& stream) +{ + auto perfomanceMap = request.GetPerformanceCounts(); + printPerformanceCounts(perfomanceMap, stream); } /** * @deprecated */ -static UNUSED void printPerformanceCountsPlugin(InferenceEngine::InferenceEnginePluginPtr plugin, std::ostream &stream) { - std::map perfomanceMap; - plugin->GetPerformanceCounts(perfomanceMap, nullptr); - printPerformanceCounts(perfomanceMap, stream); +static UNUSED void printPerformanceCountsPlugin(InferenceEngine::InferenceEnginePluginPtr plugin, + std::ostream& stream) +{ + std::map perfomanceMap; + plugin->GetPerformanceCounts(perfomanceMap, nullptr); + printPerformanceCounts(perfomanceMap, stream); } /** * @brief This class represents an object that is found by an object detection net */ -class DetectedObject { +class DetectedObject +{ public: - int objectType; - float xmin, xmax, ymin, ymax, prob; - bool difficult; - - DetectedObject(int objectType, float xmin, float ymin, float xmax, float ymax, float prob, bool difficult = false) - : objectType(objectType), xmin(xmin), xmax(xmax), ymin(ymin), ymax(ymax), prob(prob), difficult(difficult) { + int objectType; + float xmin, xmax, ymin, ymax, prob; + bool difficult; + + DetectedObject(int objectType, float xmin, float ymin, float xmax, float ymax, float prob, + bool difficult = false) + : objectType(objectType) + , xmin(xmin) + , xmax(xmax) + , ymin(ymin) + , ymax(ymax) + , prob(prob) + , difficult(difficult) + { + } + + DetectedObject(const DetectedObject& other) = default; + + static float ioU(const DetectedObject& detectedObject1_, const DetectedObject& detectedObject2_) + { + // Add small space to eliminate empty squares + float epsilon = 0; // 1e-5f; + + DetectedObject detectedObject1(detectedObject1_.objectType, (detectedObject1_.xmin - epsilon), + (detectedObject1_.ymin - epsilon), + (detectedObject1_.xmax - epsilon), + (detectedObject1_.ymax - epsilon), detectedObject1_.prob); + DetectedObject detectedObject2(detectedObject2_.objectType, (detectedObject2_.xmin + epsilon), + (detectedObject2_.ymin + epsilon), (detectedObject2_.xmax), + (detectedObject2_.ymax), detectedObject2_.prob); + + if (detectedObject1.objectType != detectedObject2.objectType) + { + // objects are different, so the result is 0 + return 0.0f; } - DetectedObject(const DetectedObject& other) = default; - - static float ioU(const DetectedObject& detectedObject1_, const DetectedObject& detectedObject2_) { - // Add small space to eliminate empty squares - float epsilon = 0; // 1e-5f; - - DetectedObject detectedObject1(detectedObject1_.objectType, - (detectedObject1_.xmin - epsilon), - (detectedObject1_.ymin - epsilon), - (detectedObject1_.xmax- epsilon), - (detectedObject1_.ymax- epsilon), detectedObject1_.prob); - DetectedObject detectedObject2(detectedObject2_.objectType, - (detectedObject2_.xmin + epsilon), - (detectedObject2_.ymin + epsilon), - (detectedObject2_.xmax), - (detectedObject2_.ymax), detectedObject2_.prob); - - if (detectedObject1.objectType != detectedObject2.objectType) { - // objects are different, so the result is 0 - return 0.0f; - } - - if (detectedObject1.xmax < detectedObject1.xmin) return 0.0; - if (detectedObject1.ymax < detectedObject1.ymin) return 0.0; - if (detectedObject2.xmax < detectedObject2.xmin) return 0.0; - if (detectedObject2.ymax < detectedObject2.ymin) return 0.0; - - - float xmin = (std::max)(detectedObject1.xmin, detectedObject2.xmin); - float ymin = (std::max)(detectedObject1.ymin, detectedObject2.ymin); - float xmax = (std::min)(detectedObject1.xmax, detectedObject2.xmax); - float ymax = (std::min)(detectedObject1.ymax, detectedObject2.ymax); - - // Caffe adds 1 to every length if the box isn't normalized. So do we... - float addendum; - if (xmax > 1 || ymax > 1) - addendum = 1; - else - addendum = 0; - - // intersection - float intr; - if ((xmax >= xmin) && (ymax >= ymin)) { - intr = (addendum + xmax - xmin) * (addendum + ymax - ymin); - } else { - intr = 0.0f; - } - - // union - float square1 = (addendum + detectedObject1.xmax - detectedObject1.xmin) * (addendum + detectedObject1.ymax - detectedObject1.ymin); - float square2 = (addendum + detectedObject2.xmax - detectedObject2.xmin) * (addendum + detectedObject2.ymax - detectedObject2.ymin); + if (detectedObject1.xmax < detectedObject1.xmin) + { + return 0.0; + } + if (detectedObject1.ymax < detectedObject1.ymin) + { + return 0.0; + } + if (detectedObject2.xmax < detectedObject2.xmin) + { + return 0.0; + } + if (detectedObject2.ymax < detectedObject2.ymin) + { + return 0.0; + } - float unn = square1 + square2 - intr; + float xmin = (std::max)(detectedObject1.xmin, detectedObject2.xmin); + float ymin = (std::max)(detectedObject1.ymin, detectedObject2.ymin); + float xmax = (std::min)(detectedObject1.xmax, detectedObject2.xmax); + float ymax = (std::min)(detectedObject1.ymax, detectedObject2.ymax); - return static_cast(intr) / unn; + // Caffe adds 1 to every length if the box isn't normalized. So do we... + float addendum; + if (xmax > 1 || ymax > 1) + { + addendum = 1; } - - DetectedObject scale(float scale_x, float scale_y) const { - return DetectedObject(objectType, xmin * scale_x, ymin * scale_y, xmax * scale_x, ymax * scale_y, prob, difficult); + else + { + addendum = 0; } -}; - -class ImageDescription { -public: - const std::list alist; - const bool check_probs; - explicit ImageDescription(const std::list &alist, bool check_probs = false) - : alist(alist), check_probs(check_probs) { + // intersection + float intr; + if ((xmax >= xmin) && (ymax >= ymin)) + { + intr = (addendum + xmax - xmin) * (addendum + ymax - ymin); + } + else + { + intr = 0.0f; } - static float ioUMultiple(const ImageDescription &detectedObjects, const ImageDescription &desiredObjects) { - const ImageDescription *detectedObjectsSmall, *detectedObjectsBig; - bool check_probs = desiredObjects.check_probs; + // union + float square1 = (addendum + detectedObject1.xmax - detectedObject1.xmin) * + (addendum + detectedObject1.ymax - detectedObject1.ymin); + float square2 = (addendum + detectedObject2.xmax - detectedObject2.xmin) * + (addendum + detectedObject2.ymax - detectedObject2.ymin); - if (detectedObjects.alist.size() < desiredObjects.alist.size()) { - detectedObjectsSmall = &detectedObjects; - detectedObjectsBig = &desiredObjects; - } else { - detectedObjectsSmall = &desiredObjects; - detectedObjectsBig = &detectedObjects; - } + float unn = square1 + square2 - intr; - std::list doS = detectedObjectsSmall->alist; - std::list doB = detectedObjectsBig->alist; - - float fullScore = 0.0f; - while (doS.size() > 0) { - float score = 0.0f; - std::list::iterator bestJ = doB.end(); - for (auto j = doB.begin(); j != doB.end(); j++) { - float curscore = DetectedObject::ioU(*doS.begin(), *j); - if (score < curscore) { - score = curscore; - bestJ = j; - } - } + return static_cast(intr) / unn; + } - float coeff = 1.0; - if (check_probs) { - if (bestJ != doB.end()) { - DetectedObject test = *bestJ; - DetectedObject test1 = *doS.begin(); - float mn = std::min((*bestJ).prob, (*doS.begin()).prob); - float mx = std::max((*bestJ).prob, (*doS.begin()).prob); + DetectedObject scale(float scale_x, float scale_y) const + { + return DetectedObject(objectType, xmin * scale_x, ymin * scale_y, xmax * scale_x, + ymax * scale_y, prob, difficult); + } +}; - coeff = mn/mx; - } - } +class ImageDescription +{ +public: + const std::list alist; + const bool check_probs; + + explicit ImageDescription(const std::list& alist, bool check_probs = false) + : alist(alist), check_probs(check_probs) + { + } + + static float ioUMultiple(const ImageDescription& detectedObjects, + const ImageDescription& desiredObjects) + { + const ImageDescription *detectedObjectsSmall, *detectedObjectsBig; + bool check_probs = desiredObjects.check_probs; + + if (detectedObjects.alist.size() < desiredObjects.alist.size()) + { + detectedObjectsSmall = &detectedObjects; + detectedObjectsBig = &desiredObjects; + } + else + { + detectedObjectsSmall = &desiredObjects; + detectedObjectsBig = &detectedObjects; + } - doS.pop_front(); - if (bestJ != doB.end()) doB.erase(bestJ); - fullScore += coeff * score; + std::list doS = detectedObjectsSmall->alist; + std::list doB = detectedObjectsBig->alist; + + float fullScore = 0.0f; + while (doS.size() > 0) + { + float score = 0.0f; + std::list::iterator bestJ = doB.end(); + for (auto j = doB.begin(); j != doB.end(); j++) + { + float curscore = DetectedObject::ioU(*doS.begin(), *j); + if (score < curscore) + { + score = curscore; + bestJ = j; } - fullScore /= detectedObjectsBig->alist.size(); - - - return fullScore; + } + + float coeff = 1.0; + if (check_probs) + { + if (bestJ != doB.end()) + { + DetectedObject test = *bestJ; + DetectedObject test1 = *doS.begin(); + float mn = std::min((*bestJ).prob, (*doS.begin()).prob); + float mx = std::max((*bestJ).prob, (*doS.begin()).prob); + + coeff = mn / mx; + } + } + + doS.pop_front(); + if (bestJ != doB.end()) + { + doB.erase(bestJ); + } + fullScore += coeff * score; } + fullScore /= detectedObjectsBig->alist.size(); - ImageDescription scale(float scale_x, float scale_y) const { - std::list slist; - for (auto& dob : alist) { - slist.push_back(dob.scale(scale_x, scale_y)); - } - return ImageDescription(slist, check_probs); + return fullScore; + } + + ImageDescription scale(float scale_x, float scale_y) const + { + std::list slist; + for (auto& dob : alist) + { + slist.push_back(dob.scale(scale_x, scale_y)); } + return ImageDescription(slist, check_probs); + } }; -struct AveragePrecisionCalculator { +struct AveragePrecisionCalculator +{ private: - enum MatchKind { - TruePositive, FalsePositive - }; + enum MatchKind + { + TruePositive, + FalsePositive + }; - /** - * Here we count all TP and FP matches for all the classes in all the images. - */ - std::map>> matches; + /** + * Here we count all TP and FP matches for all the classes in all the images. + */ + std::map>> matches; - std::map N; + std::map N; - double threshold; + double threshold; - static bool SortBBoxDescend(const DetectedObject& bbox1, const DetectedObject& bbox2) { - return bbox1.prob > bbox2.prob; - } + static bool SortBBoxDescend(const DetectedObject& bbox1, const DetectedObject& bbox2) + { + return bbox1.prob > bbox2.prob; + } - static bool SortPairDescend(const std::pair& p1, const std::pair& p2) { - return p1.first > p2.first; - } + static bool SortPairDescend(const std::pair& p1, + const std::pair& p2) + { + return p1.first > p2.first; + } public: - AveragePrecisionCalculator(double threshold) : threshold(threshold) { } - - // gt_bboxes -> des - // bboxes -> det - - void consumeImage(const ImageDescription &detectedObjects, const ImageDescription &desiredObjects) { - // Collecting IoU values - int tp = 0, fp = 0; - - std::vector visited(desiredObjects.alist.size(), false); - std::vector bboxes{ std::begin(detectedObjects.alist), std::end(detectedObjects.alist) }; - std::sort(bboxes.begin(), bboxes.end(), SortBBoxDescend); - - - for (auto&& detObj : bboxes) { - // Searching for the best match to this detection - - // Searching for desired object - float overlap_max = -1; - int jmax = -1; - auto desmax = desiredObjects.alist.end(); - - int j = 0; - for (auto desObj = desiredObjects.alist.begin(); desObj != desiredObjects.alist.end(); desObj++, j++) { - double iou = DetectedObject::ioU(detObj, *desObj); - if (iou > overlap_max) { - overlap_max = iou; - jmax = j; - desmax = desObj; - } - } - - MatchKind mk; - if (overlap_max >= threshold) { - if (!desmax->difficult) { - if (!visited[jmax]) { - mk = TruePositive; - visited[jmax] = true; - } else { - mk = FalsePositive; - } - matches[detObj.objectType].push_back(std::make_pair(detObj.prob, mk)); - } - } else { - mk = FalsePositive; - matches[detObj.objectType].push_back(std::make_pair(detObj.prob, mk)); - } + AveragePrecisionCalculator(double threshold) : threshold(threshold) + { + } + + // gt_bboxes -> des + // bboxes -> det + + void consumeImage(const ImageDescription& detectedObjects, const ImageDescription& desiredObjects) + { + // Collecting IoU values + int tp = 0, fp = 0; + + std::vector visited(desiredObjects.alist.size(), false); + std::vector bboxes{ std::begin(detectedObjects.alist), + std::end(detectedObjects.alist) }; + std::sort(bboxes.begin(), bboxes.end(), SortBBoxDescend); + + for (auto&& detObj : bboxes) + { + // Searching for the best match to this detection + + // Searching for desired object + float overlap_max = -1; + int jmax = -1; + auto desmax = desiredObjects.alist.end(); + + int j = 0; + for (auto desObj = desiredObjects.alist.begin(); desObj != desiredObjects.alist.end(); + desObj++, j++) + { + double iou = DetectedObject::ioU(detObj, *desObj); + if (iou > overlap_max) + { + overlap_max = iou; + jmax = j; + desmax = desObj; } - - for (auto desObj = desiredObjects.alist.begin(); desObj != desiredObjects.alist.end(); desObj++) { - if (!desObj->difficult) { - N[desObj->objectType]++; - } - } + } + + MatchKind mk; + if (overlap_max >= threshold) + { + if (!desmax->difficult) + { + if (!visited[jmax]) + { + mk = TruePositive; + visited[jmax] = true; + } + else + { + mk = FalsePositive; + } + matches[detObj.objectType].push_back(std::make_pair(detObj.prob, mk)); } + } + else + { + mk = FalsePositive; + matches[detObj.objectType].push_back(std::make_pair(detObj.prob, mk)); + } + } - std::map calculateAveragePrecisionPerClass() const { - /** - * Precision-to-TP curve per class (a variation of precision-to-recall curve without dividing into N) - */ - std::map> precisionToTP; - - - std::map res; - - double AP = 0; - double q = 0; - for (auto m : matches) { - // Sorting - std::sort(m.second.begin(), m.second.end(), SortPairDescend); - - int clazz = m.first; - int TP = 0, FP = 0; - - std::vector prec; - std::vector rec; - - for (auto mm : m.second) { - // Here we are descending in a probability value - MatchKind mk = mm.second; - if (mk == TruePositive) TP++; - else if (mk == FalsePositive) FP++; + for (auto desObj = desiredObjects.alist.begin(); desObj != desiredObjects.alist.end(); desObj++) + { + if (!desObj->difficult) + { + N[desObj->objectType]++; + } + } + } - double precision = static_cast(TP) / (TP + FP); - double recall = 0; - if (N.find(clazz) != N.end()) { - recall = static_cast(TP) / N.at(clazz); - } + std::map calculateAveragePrecisionPerClass() const + { + /** + * Precision-to-TP curve per class (a variation of precision-to-recall curve without dividing + * into N) + */ + std::map> precisionToTP; + + std::map res; + + double AP = 0; + double q = 0; + for (auto m : matches) + { + // Sorting + std::sort(m.second.begin(), m.second.end(), SortPairDescend); + + int clazz = m.first; + int TP = 0, FP = 0; + + std::vector prec; + std::vector rec; + + for (auto mm : m.second) + { + // Here we are descending in a probability value + MatchKind mk = mm.second; + if (mk == TruePositive) + { + TP++; + } + else if (mk == FalsePositive) + { + FP++; + } - prec.push_back(precision); - rec.push_back(recall); - } + double precision = static_cast(TP) / (TP + FP); + double recall = 0; + if (N.find(clazz) != N.end()) + { + recall = static_cast(TP) / N.at(clazz); + } - int num = rec.size(); - - // 11point from Caffe - double ap = 0; - std::vector max_precs(11, 0.); - int start_idx = num - 1; - for (int j = 10; j >= 0; --j) { - for (int i = start_idx; i >= 0; --i) { - if (rec[i] < j / 10.) { - start_idx = i; - if (j > 0) { - max_precs[j-1] = max_precs[j]; - } - break; - } else { - if (max_precs[j] < prec[i]) { - max_precs[j] = prec[i]; - } - } - } + prec.push_back(precision); + rec.push_back(recall); + } + + int num = rec.size(); + + // 11point from Caffe + double ap = 0; + std::vector max_precs(11, 0.); + int start_idx = num - 1; + for (int j = 10; j >= 0; --j) + { + for (int i = start_idx; i >= 0; --i) + { + if (rec[i] < j / 10.) + { + start_idx = i; + if (j > 0) + { + max_precs[j - 1] = max_precs[j]; } - for (int j = 10; j >= 0; --j) { - ap += max_precs[j] / 11; + break; + } + else + { + if (max_precs[j] < prec[i]) + { + max_precs[j] = prec[i]; } - res[clazz] = ap; + } } - - return res; + } + for (int j = 10; j >= 0; --j) + { + ap += max_precs[j] / 11; + } + res[clazz] = ap; } + + return res; + } }; /** @@ -980,59 +1149,48 @@ struct AveragePrecisionCalculator { * @param width - width of the rectangle * @param detectedObjects - vector of detected objects */ -static UNUSED void addRectangles(unsigned char *data, size_t height, size_t width, std::vector detectedObjects) { - std::vector colors = { - { 128, 64, 128 }, - { 232, 35, 244 }, - { 70, 70, 70 }, - { 156, 102, 102 }, - { 153, 153, 190 }, - { 153, 153, 153 }, - { 30, 170, 250 }, - { 0, 220, 220 }, - { 35, 142, 107 }, - { 152, 251, 152 }, - { 180, 130, 70 }, - { 60, 20, 220 }, - { 0, 0, 255 }, - { 142, 0, 0 }, - { 70, 0, 0 }, - { 100, 60, 0 }, - { 90, 0, 0 }, - { 230, 0, 0 }, - { 32, 11, 119 }, - { 0, 74, 111 }, - { 81, 0, 81 } - }; - - for (size_t i = 0; i < detectedObjects.size(); i++) { - int cls = detectedObjects[i].objectType % colors.size(); - - int xmin = detectedObjects[i].xmin * width; - int xmax = detectedObjects[i].xmax * width; - int ymin = detectedObjects[i].ymin * height; - int ymax = detectedObjects[i].ymax * height; - - size_t shift_first = ymin*width * 3; - size_t shift_second = ymax*width * 3; - for (int x = xmin; x < xmax; x++) { - data[shift_first + x * 3] = colors.at(cls).red(); - data[shift_first + x * 3 + 1] = colors.at(cls).green(); - data[shift_first + x * 3 + 2] = colors.at(cls).blue(); - data[shift_second + x * 3] = colors.at(cls).red(); - data[shift_second + x * 3 + 1] = colors.at(cls).green(); - data[shift_second + x * 3 + 2] = colors.at(cls).blue(); - } +static UNUSED void addRectangles(unsigned char* data, size_t height, size_t width, + std::vector detectedObjects) +{ + std::vector colors = { { 128, 64, 128 }, { 232, 35, 244 }, { 70, 70, 70 }, + { 156, 102, 102 }, { 153, 153, 190 }, { 153, 153, 153 }, + { 30, 170, 250 }, { 0, 220, 220 }, { 35, 142, 107 }, + { 152, 251, 152 }, { 180, 130, 70 }, { 60, 20, 220 }, + { 0, 0, 255 }, { 142, 0, 0 }, { 70, 0, 0 }, + { 100, 60, 0 }, { 90, 0, 0 }, { 230, 0, 0 }, + { 32, 11, 119 }, { 0, 74, 111 }, { 81, 0, 81 } }; + + for (size_t i = 0; i < detectedObjects.size(); i++) + { + int cls = detectedObjects[i].objectType % colors.size(); + + int xmin = detectedObjects[i].xmin * width; + int xmax = detectedObjects[i].xmax * width; + int ymin = detectedObjects[i].ymin * height; + int ymax = detectedObjects[i].ymax * height; + + size_t shift_first = ymin * width * 3; + size_t shift_second = ymax * width * 3; + for (int x = xmin; x < xmax; x++) + { + data[shift_first + x * 3] = colors.at(cls).red(); + data[shift_first + x * 3 + 1] = colors.at(cls).green(); + data[shift_first + x * 3 + 2] = colors.at(cls).blue(); + data[shift_second + x * 3] = colors.at(cls).red(); + data[shift_second + x * 3 + 1] = colors.at(cls).green(); + data[shift_second + x * 3 + 2] = colors.at(cls).blue(); + } - shift_first = xmin * 3; - shift_second = xmax * 3; - for (int y = ymin; y < ymax; y++) { - data[shift_first + y*width * 3] = colors.at(cls).red(); - data[shift_first + y*width * 3 + 1] = colors.at(cls).green(); - data[shift_first + y*width * 3 + 2] = colors.at(cls).blue(); - data[shift_second + y*width * 3] = colors.at(cls).red(); - data[shift_second + y*width * 3 + 1] = colors.at(cls).green(); - data[shift_second + y*width * 3 + 2] = colors.at(cls).blue(); - } + shift_first = xmin * 3; + shift_second = xmax * 3; + for (int y = ymin; y < ymax; y++) + { + data[shift_first + y * width * 3] = colors.at(cls).red(); + data[shift_first + y * width * 3 + 1] = colors.at(cls).green(); + data[shift_first + y * width * 3 + 2] = colors.at(cls).blue(); + data[shift_second + y * width * 3] = colors.at(cls).red(); + data[shift_second + y * width * 3 + 1] = colors.at(cls).green(); + data[shift_second + y * width * 3 + 2] = colors.at(cls).blue(); } + } } diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/engines/engine.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/engines/engine.hpp index 88ac3c7b..d9b74534 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/engines/engine.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/engines/engine.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for NetworkEngine class @@ -31,9 +29,11 @@ * @brief This class is used to get the infer request * from a inference plugin and an inference network */ -namespace Engines { -class Engine { - public: +namespace Engines +{ +class Engine +{ +public: /** * @brief Create an NetworkEngine instance * from a inference plugin and an inference network. @@ -43,18 +43,22 @@ class Engine { * @brief Get the inference request this instance holds. * @return The inference request this instance holds. */ - inline InferenceEngine::InferRequest::Ptr& getRequest() { return request_; } + inline InferenceEngine::InferRequest::Ptr & getRequest() + { + return request_; + } /** * @brief Set a callback function for the infer request. * @param[in] callbackToSet A lambda function as callback function. * The callback function will be called when request is finished. */ - template - void setCompletionCallback(const T& callbackToSet) { + template + void setCompletionCallback(const T & callbackToSet) + { request_->SetCompletionCallback(callbackToSet); } - private: +private: InferenceEngine::InferRequest::Ptr request_; }; } // namespace Engines diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/factory.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/factory.hpp index ab35452e..175bdcac 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/factory.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/factory.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Factory class @@ -22,7 +20,6 @@ #ifndef DYNAMIC_VINO_LIB__FACTORY_HPP_ #define DYNAMIC_VINO_LIB__FACTORY_HPP_ - #include #include @@ -38,8 +35,9 @@ * class corresponding to * the input string */ -class Factory { - public: +class Factory +{ +public: /** * @brief This function produces the derived input device class corresponding * to the input string @@ -49,7 +47,7 @@ class Factory { * @return the instance of derived input device referenced by a smart pointer */ static std::shared_ptr makeInputDeviceByName( - const std::string& input_device_name, const std::string& input_file_path=""); + const std::string& input_device_name, const std::string& input_file_path = ""); /** * @brief This function produces the derived inference plugin corresponding to * the input string @@ -61,10 +59,9 @@ class Factory { * @return the instance of derived inference plugin referenced by a smart * pointer */ - static std::unique_ptr makePluginByName( - const std::string& device_name, - const std::string& custom_cpu_library_message, - const std::string& custom_cldnn_message, bool performance_message); + static std::unique_ptr + makePluginByName(const std::string& device_name, const std::string& custom_cpu_library_message, + const std::string& custom_cldnn_message, bool performance_message); }; #endif // DYNAMIC_VINO_LIB__FACTORY_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/age_gender_detection.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/age_gender_detection.hpp index 4a6c787c..17016d35 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/age_gender_detection.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/age_gender_detection.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for AgeGenderDetection Class @@ -28,33 +26,42 @@ #include "dynamic_vino_lib/engines/engine.hpp" #include "dynamic_vino_lib/inferences/base_inference.hpp" #include "dynamic_vino_lib/models/age_gender_detection_model.hpp" -#include +#include #include #include "inference_engine.hpp" #include "opencv2/opencv.hpp" -namespace Outputs { +namespace Outputs +{ class BaseOuput; } -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class AgeGenderResult * @brief Class for storing and processing age and gender detection result. */ -class AgeGenderResult : public Result { - public: - explicit AgeGenderResult(const cv::Rect& location); +class AgeGenderResult : public Result +{ +public: + explicit AgeGenderResult(const cv::Rect & location); /** * @brief Get the age of the detected person from the result. * @return The predictea age. */ - float getAge() const {return age_;} + float getAge() const + { + return age_; + } /** * @brief Get the possibility that the detected person is a * male from the result. * @return The possibility that the detected person is a male. */ - float getMaleProbability() const {return male_prob_;} + float getMaleProbability() const + { + return male_prob_; + } float age_ = -1; float male_prob_ = -1; @@ -62,11 +69,12 @@ class AgeGenderResult : public Result { /** * @class AgeGenderDetection - * @brief Class to load age and gender detection model and perform + * @brief Class to load age and gender detection model and perform age and gender detection. */ -class AgeGenderDetection : public BaseInference { - public: +class AgeGenderDetection : public BaseInference +{ +public: using Result = dynamic_vino_lib::AgeGenderResult; AgeGenderDetection(); ~AgeGenderDetection() override; @@ -82,7 +90,7 @@ class AgeGenderDetection : public BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - bool enqueue(const cv::Mat& frame, const cv::Rect&) override; + bool enqueue(const cv::Mat & frame, const cv::Rect &) override; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -105,7 +113,7 @@ class AgeGenderDetection : public BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - const dynamic_vino_lib::Result* getLocationResult(int idx) const override; + const dynamic_vino_lib::Result * getLocationResult(int idx) const override; /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. @@ -115,10 +123,9 @@ class AgeGenderDetection : public BaseInference { * @brief Show the observed detection result either through image window * or ROS topic. */ - const void observeOutput( - const std::shared_ptr& output) override; + const void observeOutput(const std::shared_ptr & output) override; - private: +private: std::shared_ptr valid_model_; std::vector results_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/base_inference.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/base_inference.hpp index 7e737cc3..0c3052d2 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/base_inference.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/base_inference.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for BaseInference Class @@ -29,7 +27,8 @@ #include "inference_engine.hpp" #include "opencv2/opencv.hpp" -namespace Outputs { +namespace Outputs +{ class BaseOutput; } /** @@ -39,14 +38,16 @@ class BaseOutput; * @param[in] scale_factor Scale factor for loading. * @param[in] batch_index Indicates the batch index for the frame. */ -template -void matU8ToBlob(const cv::Mat& orig_image, InferenceEngine::Blob::Ptr& blob, - float scale_factor = 1.0, int batch_index = 0) { +template +void matU8ToBlob( + const cv::Mat & orig_image, InferenceEngine::Blob::Ptr & blob, + float scale_factor = 1.0, int batch_index = 0) +{ InferenceEngine::SizeVector blob_size = blob->getTensorDesc().getDims(); const size_t width = blob_size[3]; const size_t height = blob_size[2]; const size_t channels = blob_size[1]; - T* blob_data = blob->buffer().as(); + T * blob_data = blob->buffer().as(); cv::Mat resized_image(orig_image); if (width != orig_image.size().width || height != orig_image.size().height) { @@ -58,24 +59,29 @@ void matU8ToBlob(const cv::Mat& orig_image, InferenceEngine::Blob::Ptr& blob, for (size_t h = 0; h < height; h++) { for (size_t w = 0; w < width; w++) { blob_data[batchOffset + c * width * height + h * width + w] = - resized_image.at(h, w)[c] * scale_factor; + resized_image.at(h, w)[c] * scale_factor; } } } } -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class Result * @brief Base class for detection result. */ -class Result { - public: +class Result +{ +public: friend class BaseInference; - explicit Result(const cv::Rect& location); - inline const cv::Rect getLocation() const { return location_; } + explicit Result(const cv::Rect & location); + inline const cv::Rect getLocation() const + { + return location_; + } - private: +private: cv::Rect location_; }; @@ -83,8 +89,9 @@ class Result { * @class BaseInference * @brief Base class for network inference. */ -class BaseInference { - public: +class BaseInference +{ +public: BaseInference(); virtual ~BaseInference(); /** @@ -96,14 +103,18 @@ class BaseInference { * @brief Get the loaded Engine instance. * @return The loaded Engine instance. */ - inline const std::shared_ptr getEngine() const { + inline const std::shared_ptr getEngine() const + { return engine_; } /** * @brief Get the number of enqueued frames to be infered. * @return The number of enqueued frames to be infered. */ - inline const int getEnqueuedNum() const { return enqueued_frames; } + inline const int getEnqueuedNum() const + { + return enqueued_frames; + } /** * @brief Enqueue a frame to this class. * The frame will be buffered but not infered yet. @@ -112,8 +123,7 @@ class BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - virtual bool enqueue(const cv::Mat& frame, - const cv::Rect& input_frame_loc) = 0; + virtual bool enqueue(const cv::Mat & frame, const cv::Rect & input_frame_loc) = 0; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -121,8 +131,7 @@ class BaseInference { virtual bool submitRequest(); virtual bool SynchronousRequest(); - virtual const void observeOutput( - const std::shared_ptr& output) = 0; + virtual const void observeOutput(const std::shared_ptr & output) = 0; /** * @brief This function will fetch the results of the previous inference and @@ -140,29 +149,30 @@ class BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - virtual const dynamic_vino_lib::Result* getLocationResult(int idx) const = 0; + virtual const dynamic_vino_lib::Result * getLocationResult(int idx) const = 0; /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. */ virtual const std::string getName() const = 0; - protected: +protected: /** * @brief Enqueue the fram into the input blob of the target calculation * device. Check OpenVINO document for detailed information. * @return Whether this operation is successful. */ - template - bool enqueue(const cv::Mat& frame, const cv::Rect&, float scale_factor, - int batch_index, const std::string& input_name) { + template + bool enqueue( + const cv::Mat & frame, const cv::Rect &, float scale_factor, int batch_index, + const std::string & input_name) + { if (enqueued_frames == max_batch_size_) { - slog::warn << "Number of " << getName() << "input more than maximum(" - << max_batch_size_ << ") processed by inference" << slog::endl; + slog::warn << "Number of " << getName() << "input more than maximum(" << max_batch_size_ << + ") processed by inference" << slog::endl; return false; } - InferenceEngine::Blob::Ptr input_blob = - engine_->getRequest()->GetBlob(input_name); + InferenceEngine::Blob::Ptr input_blob = engine_->getRequest()->GetBlob(input_name); matU8ToBlob(frame, input_blob, scale_factor, batch_index); enqueued_frames += 1; return true; @@ -170,12 +180,13 @@ class BaseInference { /** * @brief Set the max batch size for one inference. */ - inline void setMaxBatchSize(int max_batch_size) { + inline void setMaxBatchSize(int max_batch_size) + { max_batch_size_ = max_batch_size; } std::vector results_; - private: +private: std::shared_ptr engine_; int max_batch_size_ = 1; int enqueued_frames = 0; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/emotions_detection.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/emotions_detection.hpp index 55acc87c..f305e7ba 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/emotions_detection.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/emotions_detection.hpp @@ -1,18 +1,15 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// +// 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. /** * @brief A header file with declaration for EmotionsDetection Class @@ -31,25 +28,31 @@ #include "opencv2/opencv.hpp" #include "dynamic_vino_lib/models/emotion_detection_model.hpp" -namespace Outputs { +namespace Outputs +{ class BaseOuput; } -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class EmotionResult * @brief Class for storing and processing emotion detection result. */ -class EmotionsResult : public Result { - public: +class EmotionsResult : public Result +{ +public: friend class EmotionsDetection; - explicit EmotionsResult(const cv::Rect& location); + explicit EmotionsResult(const cv::Rect & location); /** * @brief Get the emotion label of the detected person. * @return The predictea emotion label. */ - std::string getLabel() const { return label_; } + std::string getLabel() const + { + return label_; + } - private: +private: std::string label_ = ""; float confidence_ = -1; }; @@ -58,8 +61,9 @@ class EmotionsResult : public Result { * @class EmotionDetection * @brief Class to load emotion detection model and perform emotion detection. */ -class EmotionsDetection : public BaseInference { - public: +class EmotionsDetection : public BaseInference +{ +public: using Result = dynamic_vino_lib::EmotionsResult; EmotionsDetection(); ~EmotionsDetection() override; @@ -75,7 +79,7 @@ class EmotionsDetection : public BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - bool enqueue(const cv::Mat&, const cv::Rect&) override; + bool enqueue(const cv::Mat &, const cv::Rect &) override; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -98,7 +102,7 @@ class EmotionsDetection : public BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - const dynamic_vino_lib::Result* getLocationResult(int idx) const override; + const dynamic_vino_lib::Result * getLocationResult(int idx) const override; /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. @@ -108,12 +112,14 @@ class EmotionsDetection : public BaseInference { * @brief Show the observed detection result either through image window * or ROS topic. */ - const void observeOutput( - const std::shared_ptr& output) override; + const void observeOutput(const std::shared_ptr & output) override; - std::vector getResults() { return results_; }; + std::vector getResults() + { + return results_; + } - private: +private: std::shared_ptr valid_model_; std::vector results_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/face_detection.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/face_detection.hpp index aabb0ea4..12899fe6 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/face_detection.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/face_detection.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for FaceDetection Class @@ -37,22 +35,25 @@ #include "opencv2/opencv.hpp" // namespace -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class FaceDetectionResult * @brief Class for storing and processing face detection result. */ -class FaceDetectionResult : public ObjectDetectionResult { - public: - explicit FaceDetectionResult(const cv::Rect& location); +class FaceDetectionResult : public ObjectDetectionResult +{ +public: + explicit FaceDetectionResult(const cv::Rect & location); }; /** * @class FaceDetection * @brief Class to load face detection model and perform face detection. */ -class FaceDetection : public ObjectDetection { - public: +class FaceDetection : public ObjectDetection +{ +public: explicit FaceDetection(double); }; } // namespace dynamic_vino_lib diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/head_pose_detection.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/head_pose_detection.hpp index 8780af97..07173761 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/head_pose_detection.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/head_pose_detection.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for FaceDetection Class @@ -31,31 +29,43 @@ #include "inference_engine.hpp" #include "opencv2/opencv.hpp" -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class HeadPoseResult * @brief Class for storing and processing headpose detection result. */ -class HeadPoseResult : public Result { - public: +class HeadPoseResult : public Result +{ +public: friend class HeadPoseDetection; - explicit HeadPoseResult(const cv::Rect& location); + explicit HeadPoseResult(const cv::Rect & location); /** * @brief Get the yawl angle of the headpose. * @return The yawl value. */ - float getAngleY() const {return angle_y_;} + float getAngleY() const + { + return angle_y_; + } /** * @brief Get the pitch angle of the headpose. * @return The pitch value. */ - float getAngleP() const {return angle_p_;} + float getAngleP() const + { + return angle_p_; + } /** * @brief Get the roll angle of the headpose. * @return The roll value. */ - float getAngleR() const {return angle_r_;} - private: + float getAngleR() const + { + return angle_r_; + } + +private: float angle_y_ = -1; float angle_p_ = -1; float angle_r_ = -1; @@ -65,8 +75,9 @@ class HeadPoseResult : public Result { * @class HeadPoseDetection * @brief Class to load headpose detection model and perform headpose detection. */ -class HeadPoseDetection : public BaseInference { - public: +class HeadPoseDetection : public BaseInference +{ +public: using Result = dynamic_vino_lib::HeadPoseResult; HeadPoseDetection(); ~HeadPoseDetection() override; @@ -82,7 +93,7 @@ class HeadPoseDetection : public BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - bool enqueue(const cv::Mat& frame, const cv::Rect&) override; + bool enqueue(const cv::Mat & frame, const cv::Rect &) override; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -105,7 +116,7 @@ class HeadPoseDetection : public BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - const dynamic_vino_lib::Result* getLocationResult(int idx) const override; + const dynamic_vino_lib::Result * getLocationResult(int idx) const override; /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. @@ -115,12 +126,14 @@ class HeadPoseDetection : public BaseInference { * @brief Show the observed detection result either through image window or ROS topic. */ - const void observeOutput( - const std::shared_ptr& output) override; - - std::vector getResults() { return results_; }; + const void observeOutput(const std::shared_ptr & output) override; + + std::vector getResults() + { + return results_; + } - private: +private: std::shared_ptr valid_model_; std::vector results_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_detection.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_detection.hpp index 96f03884..416ea317 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_detection.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_detection.hpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief A header file with declaration for ObjectDetection Class * @file object_detection.hpp @@ -33,22 +32,31 @@ #include "inference_engine.hpp" #include "opencv2/opencv.hpp" // namespace -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class ObjectDetectionResult * @brief Class for storing and processing face detection result. */ -class ObjectDetectionResult : public Result { - public: +class ObjectDetectionResult : public Result +{ +public: friend class ObjectDetection; - explicit ObjectDetectionResult(const cv::Rect& location); - std::string getLabel() const { return label_; } + explicit ObjectDetectionResult(const cv::Rect & location); + std::string getLabel() const + { + return label_; + } /** * @brief Get the confidence that the detected area is a face. - * @return The confidence value. + * @return The confidence value. */ - float getConfidence() const { return confidence_; } - private: + float getConfidence() const + { + return confidence_; + } + +private: std::string label_ = ""; float confidence_ = -1; }; @@ -56,8 +64,9 @@ class ObjectDetectionResult : public Result { * @class ObjectDetection * @brief Class to load face detection model and perform face detection. */ -class ObjectDetection : public BaseInference { - public: +class ObjectDetection : public BaseInference +{ +public: using Result = dynamic_vino_lib::ObjectDetectionResult; explicit ObjectDetection(double); ~ObjectDetection() override; @@ -73,7 +82,7 @@ class ObjectDetection : public BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - bool enqueue(const cv::Mat&, const cv::Rect&) override; + bool enqueue(const cv::Mat &, const cv::Rect &) override; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -96,19 +105,19 @@ class ObjectDetection : public BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - const dynamic_vino_lib::Result* getLocationResult(int idx) const override; + const dynamic_vino_lib::Result * getLocationResult(int idx) const override; /** * @brief Show the observed detection result either through image window or ROS topic. */ - const void observeOutput(const std::shared_ptr& output); + const void observeOutput(const std::shared_ptr & output); /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. */ const std::string getName() const override; - private: +private: std::shared_ptr valid_model_; std::vector results_; int width_ = 0; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_segmentation.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_segmentation.hpp index 890d88c1..3fde54c6 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_segmentation.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inferences/object_segmentation.hpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief A header file with declaration for ObjectSegmentation Class * @file object_detection.hpp @@ -32,23 +31,35 @@ #include "inference_engine.hpp" #include "opencv2/opencv.hpp" // namespace -namespace dynamic_vino_lib { +namespace dynamic_vino_lib +{ /** * @class ObjectSegmentationResult * @brief Class for storing and processing object segmentation result. */ -class ObjectSegmentationResult : public Result { - public: +class ObjectSegmentationResult : public Result +{ +public: friend class ObjectSegmentation; - explicit ObjectSegmentationResult(const cv::Rect& location); - std::string getLabel() const { return label_; } + explicit ObjectSegmentationResult(const cv::Rect & location); + std::string getLabel() const + { + return label_; + } /** * @brief Get the confidence that the detected area is a face. - * @return The confidence value. + * @return The confidence value. */ - float getConfidence() const { return confidence_; } - cv::Mat getMask() const { return mask_; } - private: + float getConfidence() const + { + return confidence_; + } + cv::Mat getMask() const + { + return mask_; + } + +private: std::string label_ = ""; float confidence_ = -1; cv::Mat mask_; @@ -57,8 +68,9 @@ class ObjectSegmentationResult : public Result { * @class ObjectSegmentation * @brief Class to load object segmentation model and perform object segmentation. */ -class ObjectSegmentation : public BaseInference { - public: +class ObjectSegmentation : public BaseInference +{ +public: using Result = dynamic_vino_lib::ObjectSegmentationResult; explicit ObjectSegmentation(double); ~ObjectSegmentation() override; @@ -74,7 +86,7 @@ class ObjectSegmentation : public BaseInference { * to the frame generated by the input device. * @return Whether this operation is successful. */ - bool enqueue(const cv::Mat&, const cv::Rect&) override; + bool enqueue(const cv::Mat &, const cv::Rect &) override; /** * @brief Start inference for all buffered frames. * @return Whether this operation is successful. @@ -97,18 +109,19 @@ class ObjectSegmentation : public BaseInference { * to the frame generated by the input device. * @param[in] idx The index of the result. */ - const dynamic_vino_lib::Result* getLocationResult(int idx) const override; + const dynamic_vino_lib::Result * getLocationResult(int idx) const override; /** * @brief Show the observed detection result either through image window or ROS topic. */ - const void observeOutput(const std::shared_ptr& output); + const void observeOutput(const std::shared_ptr & output); /** * @brief Get the name of the Inference instance. * @return The name of the Inference instance. */ const std::string getName() const override; - private: + +private: std::shared_ptr valid_model_; std::vector results_; int width_ = 0; @@ -116,4 +129,4 @@ class ObjectSegmentation : public BaseInference { double show_output_thresh_ = 0; }; } // namespace dynamic_vino_lib -#endif // DYNAMIC_VINO_LIB__INFERENCES__OBJECT_SEGMENTATION_HPP_ \ No newline at end of file +#endif // DYNAMIC_VINO_LIB__INFERENCES__OBJECT_SEGMENTATION_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/base_input.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/base_input.hpp index 135a4714..bbe3f724 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/base_input.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/base_input.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for BaseInput Class @@ -29,9 +27,11 @@ * @brief This class is an interface for three kinds of * input devices: realsense camera, standard camera and video */ -namespace Input { -class BaseInputDevice : public Ros2Handler { - public: +namespace Input +{ +class BaseInputDevice : public Ros2Handler +{ +public: /** * @brief Initialize the input device, * for cameras, it will turn the camera on and get ready to read frames, @@ -54,8 +54,11 @@ class BaseInputDevice : public Ros2Handler { * @brief Read next frame, and give the value to argument frame. * @return Whether the next frame is successfully read. */ - virtual bool read(cv::Mat* frame) = 0; - virtual bool readService(cv::Mat* frame, std::string config_path) { return true;}; + virtual bool read(cv::Mat * frame) = 0; + virtual bool readService(cv::Mat * frame, std::string config_path) + { + return true; + } virtual void config() = 0; //< TODO virtual ~BaseInputDevice() = default; @@ -63,45 +66,68 @@ class BaseInputDevice : public Ros2Handler { * @brief Get the width of the frame read from input device. * @return The width of the frame read from input device. */ - inline size_t getWidth() { return width_; } + inline size_t getWidth() + { + return width_; + } /** * @brief Set the width of the frame read from input device. * @param[in] width Width to be set for the frame. */ - inline void setWidth(size_t width) { width_ = width; } + inline void setWidth(size_t width) + { + width_ = width; + } /** * @brief Get the height of the frame read from input device. * @return The height of the frame read from input device. */ - inline size_t getHeight() { return height_; } + inline size_t getHeight() + { + return height_; + } /** * @brief Set the height of the frame read from input device. * @param[in] width Width to be set for the frame. */ - inline void setHeight(size_t height) { height_ = height; } + inline void setHeight(size_t height) + { + height_ = height; + } /** * @brief Check whether the input device is successfully initiated. * @return Whether the input device is successfully initiated. */ - inline bool isInit() { return is_init_; } + inline bool isInit() + { + return is_init_; + } /** * @brief Set the initialization state for input device. * @param[in] is_init The initialization state to be set. */ - inline void setInitStatus(bool is_init) { is_init_ = is_init; } + inline void setInitStatus(bool is_init) + { + is_init_ = is_init; + } /** * @brief Set the frame_id of input device for ROSTopic outputs. * @param[in] frame_id The frame_id of input device. */ - inline void setFrameID(std::string frame_id) {frame_id_ = frame_id; } + inline void setFrameID(std::string frame_id) + { + frame_id_ = frame_id; + } /** * @brief Get the frame_id of input device. * @return Frame_id of input device. */ - inline std::string getFrameID() { return frame_id_; } - + inline std::string getFrameID() + { + return frame_id_; + } - private: +private: size_t width_ = 0; size_t height_ = 0; bool is_init_ = false; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/image_input.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/image_input.hpp index 193c228c..9b9f0832 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/image_input.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/image_input.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for Image class @@ -25,14 +23,16 @@ #include #include "dynamic_vino_lib/inputs/base_input.hpp" -namespace Input { +namespace Input +{ /** * @class Image * @brief Class for recieving an image file as input. */ -class Image : public BaseInputDevice { - public: - explicit Image(const std::string&); +class Image : public BaseInputDevice +{ +public: + explicit Image(const std::string &); /** * @brief Read an image file from the file path. * @param[in] An image file path. @@ -44,22 +44,28 @@ class Image : public BaseInputDevice { * No implementation for Image class. * @return Whether the input device is successfully turned on. */ - bool initialize(int t) override { return initialize(); }; + bool initialize(int t) override + { + return initialize(); + } /** * @brief Initialize the input device with given width and height. * No implementation for Image class. * @return Whether the input device is successfully turned on. */ - bool initialize(size_t width, size_t height) override { return initialize(); }; + bool initialize(size_t width, size_t height) override + { + return initialize(); + } /** * @brief Read next frame, and give the value to argument frame. * @return Whether the next frame is successfully read. */ - bool read(cv::Mat* frame) override; - bool readService(cv::Mat* frame, std::string config_path); + bool read(cv::Mat * frame) override; + bool readService(cv::Mat * frame, std::string config_path); void config() override; - private: +private: cv::Mat image_; std::string file_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera.hpp index 4f04fbb5..03d1bbc6 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for RealSenseCamera class @@ -26,15 +24,17 @@ #include #include "dynamic_vino_lib/inputs/base_input.hpp" -namespace Input { +namespace Input +{ /** * @class RealSenseCamera * @brief Class for recieving a realsense camera as input. */ -class RealSenseCamera : public BaseInputDevice { - public: +class RealSenseCamera : public BaseInputDevice +{ +public: /** - * @brief Initialize the input device, turn the + * @brief Initialize the input device, turn the * camera on and get ready to read frames. * @return Whether the input device is successfully turned on. */ @@ -44,7 +44,10 @@ class RealSenseCamera : public BaseInputDevice { * Initialize camera by its index when multiple standard camera is connected. * @return Whether the input device is successfully turned on. */ - bool initialize(int t) override { return true; }; + bool initialize(int t) override + { + return true; + } /** * @brief Initialize the input device with given width and height. * @return Whether the input device is successfully turned on. @@ -54,10 +57,10 @@ class RealSenseCamera : public BaseInputDevice { * @brief Read next frame, and give the value to argument frame. * @return Whether the next frame is successfully read. */ - bool read(cv::Mat* frame) override; + bool read(cv::Mat * frame) override; void config() override; - private: +private: rs2::config cfg_; rs2::pipeline pipe_; bool first_read_ = true; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera_topic.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera_topic.hpp index dd96bab9..b76a692c 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera_topic.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/realsense_camera_topic.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for RealSenseCamera class @@ -30,21 +28,29 @@ #include "dynamic_vino_lib/inputs/base_input.hpp" -namespace Input { +namespace Input +{ /** * @class RealSenseCameraTopic * @brief Class for recieving a realsense camera topic as input. */ -class RealSenseCameraTopic : public BaseInputDevice, public rclcpp::Node{ - public: +class RealSenseCameraTopic : public BaseInputDevice, public rclcpp::Node +{ +public: RealSenseCameraTopic(); bool initialize() override; - bool initialize(int t) override { return true; }; - bool initialize(size_t width, size_t height) override { return true; }; - bool read(cv::Mat* frame) override; + bool initialize(int t) override + { + return true; + } + bool initialize(size_t width, size_t height) override + { + return true; + } + bool read(cv::Mat * frame) override; void config() override; - private: +private: rclcpp::Subscription::SharedPtr sub_; cv::Mat image; int image_count; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/ros2_handler.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/ros2_handler.hpp index cdd2be7e..0423595f 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/ros2_handler.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/ros2_handler.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for ROS/ROS2 handler @@ -24,22 +22,24 @@ #include -namespace Input { - -class Ros2Handler { +namespace Input +{ +class Ros2Handler +{ public: - void setHandler(const std::shared_ptr& node) { + void setHandler(const std::shared_ptr & node) + { node_ = node; } - std::shared_ptr getHandler() const{ + std::shared_ptr getHandler() const + { return node_; } + private: std::shared_ptr node_; - }; - -} // namespace +} // namespace -#endif // DYNAMIC_VINO_LIB__INPUTS__ROS_HANDLER_HPP_ \ No newline at end of file +#endif // DYNAMIC_VINO_LIB__INPUTS__ROS_HANDLER_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/standard_camera.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/standard_camera.hpp index 9100b60f..0af9ee81 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/standard_camera.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/standard_camera.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for StandardCamera class @@ -22,17 +20,18 @@ #ifndef DYNAMIC_VINO_LIB__INPUTS__STANDARD_CAMERA_HPP_ #define DYNAMIC_VINO_LIB__INPUTS__STANDARD_CAMERA_HPP_ - #include #include "dynamic_vino_lib/inputs/base_input.hpp" -namespace Input { +namespace Input +{ /** * @class StandardCamera * @brief Class for recieving a standard camera as input. */ -class StandardCamera : public BaseInputDevice { - public: +class StandardCamera : public BaseInputDevice +{ +public: /** * @brief Initialize the input device, * for cameras, it will turn the camera on and get ready to read frames, @@ -55,10 +54,10 @@ class StandardCamera : public BaseInputDevice { * @brief Read next frame, and give the value to argument frame. * @return Whether the next frame is successfully read. */ - bool read(cv::Mat* frame) override; + bool read(cv::Mat * frame) override; void config() override; - private: +private: cv::VideoCapture cap; }; } // namespace Input diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/video_input.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/video_input.hpp index 8b730fa4..5ac76863 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/inputs/video_input.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/inputs/video_input.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for Video class @@ -25,14 +23,16 @@ #include #include "dynamic_vino_lib/inputs/base_input.hpp" -namespace Input { +namespace Input +{ /** * @class Video * @brief Class for recieving a video file as input. */ -class Video : public BaseInputDevice { - public: - explicit Video(const std::string&); +class Video : public BaseInputDevice +{ +public: + explicit Video(const std::string &); /** * @brief Read a video file from the file path. * @param[in] An video file path. @@ -44,7 +44,10 @@ class Video : public BaseInputDevice { * No implementation for Video class. * @return Whether the input device is successfully turned on. */ - bool initialize(int t) override { return initialize(); }; + bool initialize(int t) override + { + return initialize(); + } /** * @brief Initialize the input device with given width and height. * No implementation for Video class. @@ -55,10 +58,10 @@ class Video : public BaseInputDevice { * @brief Read next frame, and give the value to argument frame. * @return Whether the next frame is successfully read. */ - bool read(cv::Mat* frame) override; + bool read(cv::Mat * frame) override; void config() override; - private: +private: cv::VideoCapture cap; std::string video_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/age_gender_detection_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/age_gender_detection_model.hpp index 2b6f43b7..bb096910 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/age_gender_detection_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/age_gender_detection_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for AgeGenderDetectionModel Class @@ -25,29 +23,38 @@ #include #include "dynamic_vino_lib/models/base_model.hpp" -namespace Models { +namespace Models +{ /** * @class AgeGenderDetectionModel * @brief This class generates the age gender detection model. */ -class AgeGenderDetectionModel : public BaseModel { - public: - AgeGenderDetectionModel(const std::string&, int, int, int); +class AgeGenderDetectionModel : public BaseModel +{ +public: + AgeGenderDetectionModel(const std::string &, int, int, int); /** * @brief Get the input name. * @return Input name. */ - inline const std::string getInputName() const { return input_; } + inline const std::string getInputName() const + { + return input_; + } /** * @brief Get the age from the detection reuslt. * @return Detected age. */ - inline const std::string getOutputAgeName() const { return output_age_; } + inline const std::string getOutputAgeName() const + { + return output_age_; + } /** * @brief Get the gender from the detection reuslt. * @return Detected gender. */ - inline const std::string getOutputGenderName() const { + inline const std::string getOutputGenderName() const + { return output_gender_; } /** @@ -56,11 +63,11 @@ class AgeGenderDetectionModel : public BaseModel { */ const std::string getModelName() const override; - protected: - void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr&) override; +protected: + void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr &) override; void setLayerProperty(InferenceEngine::CNNNetReader::Ptr) override; - private: +private: std::string input_; std::string output_age_; std::string output_gender_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/base_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/base_model.hpp index 54e6d0f8..7fe1681d 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/base_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/base_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for BaseModel Class @@ -28,19 +26,22 @@ #include "inference_engine.hpp" -namespace Engines { +namespace Engines +{ class Engine; } -namespace Models { +namespace Models +{ /** * @class BaseModel * @brief This class represents the network given by .xml and .bin file */ -class BaseModel { +class BaseModel +{ using Ptr = std::shared_ptr; - public: +public: /** * @brief Initialize the class with given .xml, .bin and .labels file. It will * also check whether the number of input and output are fit. @@ -51,18 +52,23 @@ class BaseModel { * @param[in] batch_size The number of batch size the network should have. * @return Whether the input device is successfully turned on. */ - BaseModel(const std::string& model_loc, int input_num, int output_num, - int batch_size); + BaseModel(const std::string & model_loc, int input_num, int output_num, int batch_size); /** * @brief Get the label vector. * @return The label vector. */ - inline std::vector& getLabels() { return labels_; } + inline std::vector & getLabels() + { + return labels_; + } /** * @brief Get the maximum batch size of the model. * @return The maximum batch size of the model. */ - inline const int getMaxBatchSize() const { return max_batch_size_; } + inline const int getMaxBatchSize() const + { + return max_batch_size_; + } /** * @brief Initialize the model. During the process the class will check * the network input, output size, check layer property and @@ -75,23 +81,21 @@ class BaseModel { */ virtual const std::string getModelName() const = 0; - protected: +protected: /** * @brief Check whether the layer property * (output layer name, output layer type, etc.) is right * @param[in] network_reader The reader of the network to be checked. */ - virtual void checkLayerProperty( - const InferenceEngine::CNNNetReader::Ptr& network_reader) = 0; + virtual void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr & network_reader) = 0; /** * @brief Set the layer property (layer layout, layer precision, etc.). * @param[in] network_reader The reader of the network to be set. */ - virtual void - setLayerProperty(InferenceEngine::CNNNetReader::Ptr network_reader) = 0; + virtual void setLayerProperty(InferenceEngine::CNNNetReader::Ptr network_reader) = 0; virtual void checkNetworkSize(int, int, InferenceEngine::CNNNetReader::Ptr); - private: +private: friend class Engines::Engine; InferenceEngine::CNNNetReader::Ptr net_reader_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/emotion_detection_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/emotion_detection_model.hpp index 949152c9..0624ac51 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/emotion_detection_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/emotion_detection_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for EmotionDetectionModel Class @@ -25,27 +23,35 @@ #include #include "dynamic_vino_lib/models/base_model.hpp" -namespace Models { +namespace Models +{ /** * @class EmotionDetectionModel * @brief This class generates the emotion detection model. */ -class EmotionDetectionModel : public BaseModel { - public: - EmotionDetectionModel(const std::string&, int, int, int); - inline const std::string getInputName() { return input_; } - inline const std::string getOutputName() { return output_; } +class EmotionDetectionModel : public BaseModel +{ +public: + EmotionDetectionModel(const std::string &, int, int, int); + inline const std::string getInputName() + { + return input_; + } + inline const std::string getOutputName() + { + return output_; + } /** * @brief Get the name of this detection model. * @return Name of the model. */ const std::string getModelName() const override; - protected: - void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr&) override; +protected: + void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr &) override; void setLayerProperty(InferenceEngine::CNNNetReader::Ptr) override; - private: +private: std::string input_; std::string output_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/face_detection_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/face_detection_model.hpp index 02196de3..acd24142 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/face_detection_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/face_detection_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for FaceDetectionModel Class @@ -25,14 +23,16 @@ #include #include "dynamic_vino_lib/models/object_detection_model.hpp" -namespace Models { +namespace Models +{ /** * @class FaceDetectionModel * @brief This class generates the face detection model. */ -class FaceDetectionModel : public ObjectDetectionModel { - public: - FaceDetectionModel(const std::string&, int, int, int); +class FaceDetectionModel : public ObjectDetectionModel +{ +public: + FaceDetectionModel(const std::string &, int, int, int); /** * @brief Get the name of this detection model. * @return Name of the model. diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/head_pose_detection_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/head_pose_detection_model.hpp index 412b8b19..0084bc31 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/head_pose_detection_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/head_pose_detection_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for HeadPoseDetectionModel Class @@ -25,34 +23,42 @@ #include #include "dynamic_vino_lib/models/base_model.hpp" -namespace Models { +namespace Models +{ /** * @class HeadPoseDetectionModel * @brief This class generates the headpose detection model. */ -class HeadPoseDetectionModel : public BaseModel { - public: - HeadPoseDetectionModel(const std::string&, int, int, int); - inline const std::string getInputName() const { return input_; } +class HeadPoseDetectionModel : public BaseModel +{ +public: + HeadPoseDetectionModel(const std::string &, int, int, int); + inline const std::string getInputName() const + { + return input_; + } /** * @brief Get the output angle roll. * @return Roll value. */ - inline const std::string getOutputOutputAngleR() const { + inline const std::string getOutputOutputAngleR() const + { return output_angle_r_; } /** * @brief Get the output angle pitch. * @return Pitch value. */ - inline const std::string getOutputOutputAngleP() const { + inline const std::string getOutputOutputAngleP() const + { return output_angle_p_; } /** * @brief Get the output angle yawl. * @return Yawl value. */ - inline const std::string getOutputOutputAngleY() const { + inline const std::string getOutputOutputAngleY() const + { return output_angle_y_; } /** @@ -61,11 +67,11 @@ class HeadPoseDetectionModel : public BaseModel { */ const std::string getModelName() const override; - protected: - void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr&) override; +protected: + void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr &) override; void setLayerProperty(InferenceEngine::CNNNetReader::Ptr) override; - private: +private: std::string input_; std::string output_angle_r_ = "angle_r_fc"; std::string output_angle_p_ = "angle_p_fc"; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/object_detection_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/object_detection_model.hpp index db1f5962..c2a3b8cd 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/object_detection_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/object_detection_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for ObjectDetectionModel Class * @file face_detection_model.h @@ -21,25 +19,40 @@ #define DYNAMIC_VINO_LIB__MODELS__OBJECT_DETECTION_MODEL_HPP_ #include #include "dynamic_vino_lib/models/base_model.hpp" -namespace Models { +namespace Models +{ /** * @class ObjectDetectionModel * @brief This class generates the face detection model. */ -class ObjectDetectionModel : public BaseModel { - public: - ObjectDetectionModel(const std::string&, int, int, int); - inline const int getMaxProposalCount() { return max_proposal_count_; } - inline const int getObjectSize() { return object_size_; } - inline const std::string getInputName() { return input_; } - inline const std::string getOutputName() { return output_; } +class ObjectDetectionModel : public BaseModel +{ +public: + ObjectDetectionModel(const std::string &, int, int, int); + inline const int getMaxProposalCount() + { + return max_proposal_count_; + } + inline const int getObjectSize() + { + return object_size_; + } + inline const std::string getInputName() + { + return input_; + } + inline const std::string getOutputName() + { + return output_; + } /** * @brief Get the name of this detection model. * @return Name of the model. */ const std::string getModelName() const override; - protected: - void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr&) override; + +protected: + void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr &) override; void setLayerProperty(InferenceEngine::CNNNetReader::Ptr) override; int max_proposal_count_; @@ -48,4 +61,4 @@ class ObjectDetectionModel : public BaseModel { std::string output_; }; } // namespace Models -#endif // DYNAMIC_VINO_LIB__MODELS__OBJECT_DETECTION_MODEL_HPP_ \ No newline at end of file +#endif // DYNAMIC_VINO_LIB__MODELS__OBJECT_DETECTION_MODEL_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/models/object_segmentation_model.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/models/object_segmentation_model.hpp index 1e07a508..196b82ac 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/models/object_segmentation_model.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/models/object_segmentation_model.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for ObjectSegmentationModel Class * @file face_detection_model.h @@ -21,30 +19,49 @@ #define DYNAMIC_VINO_LIB__MODELS__OBJECT_SEGMENTATION_MODEL_HPP_ #include #include "dynamic_vino_lib/models/base_model.hpp" -namespace Models { +namespace Models +{ /** * @class ObjectSegmentationModel * @brief This class generates the object segmentation model. */ -class ObjectSegmentationModel : public BaseModel { - public: - ObjectSegmentationModel(const std::string&, int, int, int); - inline const int getMaxProposalCount() { return max_proposal_count_; } - inline const int getObjectSize() { return object_size_; } - inline const std::string getInputName() { return input_; } - inline const std::string getDetectionOutputName() { return detection_output_; } - inline const std::string getMaskOutputName() { return mask_output_; } +class ObjectSegmentationModel : public BaseModel +{ +public: + ObjectSegmentationModel(const std::string &, int, int, int); + inline const int getMaxProposalCount() + { + return max_proposal_count_; + } + inline const int getObjectSize() + { + return object_size_; + } + inline const std::string getInputName() + { + return input_; + } + inline const std::string getDetectionOutputName() + { + return detection_output_; + } + inline const std::string getMaskOutputName() + { + return mask_output_; + } /** * @brief Get the name of this segmentation model. * @return Name of the model. */ const std::string getModelName() const override; - protected: - void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr&) override; + +protected: + void checkLayerProperty(const InferenceEngine::CNNNetReader::Ptr &) override; void setLayerProperty(InferenceEngine::CNNNetReader::Ptr) override; void checkNetworkSize(int, int, InferenceEngine::CNNNetReader::Ptr) override; - private: + +private: int max_proposal_count_; int object_size_; std::string input_; @@ -52,4 +69,4 @@ class ObjectSegmentationModel : public BaseModel { std::string detection_output_; }; } // namespace Models -#endif // DYNAMIC_VINO_LIB__MODELS__OBJECT_SEGMENTATION_MODEL_HPP_ \ No newline at end of file +#endif // DYNAMIC_VINO_LIB__MODELS__OBJECT_SEGMENTATION_MODEL_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/base_output.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/base_output.hpp index 8d8bd02f..a2197f4e 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/base_output.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/base_output.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for HeadPoseDetectionModel Class @@ -23,7 +21,7 @@ #define DYNAMIC_VINO_LIB__OUTPUTS__BASE_OUTPUT_HPP_ #include -#include +#include #include #include #include @@ -47,49 +45,62 @@ #include "opencv2/opencv.hpp" class Pipeline; -namespace Outputs { +namespace Outputs +{ /** * @class BaseOutput * @brief This class is a base class for various output devices. It employs * visitor pattern to perform different operations to different inference * result with different output device */ -class BaseOutput { - public: +class BaseOutput +{ +public: BaseOutput() = default; /** * @brief Generate output content according to the object segmentation result. */ - virtual void accept( - const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Generate output content according to the object detection result. */ - virtual void accept( - const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Generate output content according to the face detection result. */ - virtual void accept( - const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Generate output content according to the emotion detection result. */ - virtual void accept(const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Generate output content according to the age and gender detection * result. */ - virtual void accept(const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Generate output content according to the headpose detection result. */ - virtual void accept(const std::vector&) {} + virtual void accept(const std::vector &) + { + } /** * @brief Calculate the camera matrix of a frame for image window output, no implementation for ros topic output. */ - virtual void feedFrame(const cv::Mat&) {} + virtual void feedFrame(const cv::Mat &) + { + } /** * @brief Show all the contents generated by the accept functions. */ @@ -99,17 +110,17 @@ class BaseOutput { */ int getFPS() const; - void setPipeline(Pipeline* const pipeline); - virtual void setResponse(std::shared_ptr response) {} ; - virtual void setResponse(std::shared_ptr response) {} ; - virtual void setResponse(std::shared_ptr response) {} ; - virtual void setResponse(std::shared_ptr response) {} ; - Pipeline* getPipeline() const; + void setPipeline(Pipeline * const pipeline); + virtual void setResponse(std::shared_ptr response) {} + virtual void setResponse(std::shared_ptr response) {} + virtual void setResponse(std::shared_ptr response) {} + virtual void setResponse(std::shared_ptr response) {} + Pipeline * getPipeline() const; cv::Mat getFrame() const; - protected: +protected: cv::Mat frame_; - Pipeline* pipeline_; + Pipeline * pipeline_; }; } // namespace Outputs #endif // DYNAMIC_VINO_LIB__OUTPUTS__BASE_OUTPUT_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/image_window_output.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/image_window_output.hpp index 892d2e9e..71a13c91 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/image_window_output.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/image_window_output.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for ImageWindowOutput Class @@ -26,22 +24,23 @@ #include #include "dynamic_vino_lib/outputs/base_output.hpp" -namespace Outputs { +namespace Outputs +{ /** * @class ImageWindowOutput * @brief This class handles and shows the detection result with image window. */ -class ImageWindowOutput : public BaseOutput { - public: - explicit ImageWindowOutput(const std::string& window_name, - int focal_length = 950); +class ImageWindowOutput : public BaseOutput +{ +public: + explicit ImageWindowOutput(const std::string & window_name, int focal_length = 950); /** * @brief Calculate the camera matrix of a frame for image * window output. * @param[in] A frame. */ - void feedFrame(const cv::Mat&) override; - + void feedFrame(const cv::Mat &) override; + /** * @brief Decorate frame according to detection result */ @@ -55,71 +54,65 @@ class ImageWindowOutput : public BaseOutput { * the object segmentation result. * @param[in] An obejct segmentation result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate image window output content according to * the face detection result. * @param[in] A face detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate image window output content according to * the object detection result. * @param[in] results A bundle of object detection results. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate image window output content according to * the emotion detection result. * @param[in] A emotion detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate image window output content according to * the age and gender detection result. * @param[in] A head pose detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate image window output content according to * the headpose detection result. * @param[in] An age gender detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; - private: - void initOutputs(unsigned size); - /** - * @brief Calculate the axises of the coordinates for showing - * the image window. - */ - cv::Point calcAxis(cv::Mat r, double cx, double cy, double cz, cv::Point cp); - /** - * @brief Calculte the rotation transform from the rotation pose. - * @param[in] yaw Yaw rotation value. - * @param[in] pitch Pitch rotation value. - * @param[in] roll Roll rotation value. - */ - cv::Mat getRotationTransform(double yaw, double pitch, double roll); +private: + void initOutputs(unsigned size); + /** + * @brief Calculate the axises of the coordinates for showing + * the image window. + */ + cv::Point calcAxis(cv::Mat r, double cx, double cy, double cz, cv::Point cp); + /** + * @brief Calculte the rotation transform from the rotation pose. + * @param[in] yaw Yaw rotation value. + * @param[in] pitch Pitch rotation value. + * @param[in] roll Roll rotation value. + */ + cv::Mat getRotationTransform(double yaw, double pitch, double roll); - void mergeMask( - const std::vector&); + void mergeMask(const std::vector &); - struct OutputData { + struct OutputData + { std::string desc; cv::Rect rect; cv::Scalar scalar; - cv::Point hp_cp; // for headpose, center point - cv::Point hp_x; // for headpose, end point of xAxis - cv::Point hp_y; // for headpose, end point of yAxis - cv::Point hp_zs; // for headpose, start point of zAxis - cv::Point hp_ze; // for headpose, end point of zAxis + cv::Point hp_cp; // for headpose, center point + cv::Point hp_x; // for headpose, end point of xAxis + cv::Point hp_y; // for headpose, end point of yAxis + cv::Point hp_zs; // for headpose, start point of zAxis + cv::Point hp_ze; // for headpose, end point of zAxis }; std::vector outputs_; @@ -127,27 +120,11 @@ class ImageWindowOutput : public BaseOutput { float focal_length_; cv::Mat camera_matrix_; std::vector> colors_ = { - {128, 64, 128}, - {232, 35, 244}, - {70, 70, 70}, - {156, 102, 102}, - {153, 153, 190}, - {153, 153, 153}, - {30, 170, 250}, - {0, 220, 220}, - {35, 142, 107}, - {152, 251, 152}, - {180, 130, 70}, - {60, 20, 220}, - {0, 0, 255}, - {142, 0, 0}, - {70, 0, 0}, - {100, 60, 0}, - {90, 0, 0}, - {230, 0, 0}, - {32, 11, 119}, - {0, 74, 111}, - {81, 0, 81} + {128, 64, 128}, {232, 35, 244}, {70, 70, 70}, {156, 102, 102}, {153, 153, 190}, + {153, 153, 153}, {30, 170, 250}, {0, 220, 220}, {35, 142, 107}, {152, 251, 152}, + {180, 130, 70}, {60, 20, 220}, {0, 0, 255}, {142, 0, 0}, {70, 0, 0}, + {100, 60, 0}, {90, 0, 0}, {230, 0, 0}, {32, 11, 119}, {0, 74, 111}, + {81, 0, 81} }; }; } // namespace Outputs diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_service_output.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_service_output.hpp index 128362cf..4af36622 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_service_output.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_service_output.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for RosTopicOutput Class @@ -41,59 +39,59 @@ #include "dynamic_vino_lib/inferences/face_detection.hpp" #include "dynamic_vino_lib/outputs/base_output.hpp" -namespace Outputs { +namespace Outputs +{ /** * @class RosTopicOutput * @brief This class handles and publish the detection result with ros topic. */ -class RosServiceOutput : public BaseOutput { - public: - RosServiceOutput() { }; +class RosServiceOutput : public BaseOutput +{ +public: + RosServiceOutput() {} /** * @brief Calculate the camera matrix of a frame. * @param[in] A frame. */ - void feedFrame(const cv::Mat&) override; + void feedFrame(const cv::Mat &) override; /** * @brief Publish all the detected infomations generated by the accept * functions with ros topic. */ - void handleOutput() override { }; + void handleOutput() override {} /** * @brief Generate ros topic infomation according to * the object detection result. * @param[in] results a bundle of object detection results. */ - void accept( - const std::vector& results) override; + void accept(const std::vector & results) override; /** * @brief Generate ros topic infomation according to * the face detection result. * @param[in] An face detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the emotion detection result. * @param[in] An emotion detection result objetc. */ - void accept(const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the age gender detection result. * @param[in] An age gender detection result objetc. */ - void accept(const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the headpose detection result. * @param[in] An head pose detection result objetc. */ - void accept(const std::vector&) override; - void setResponse(std::shared_ptr response) ; + void accept(const std::vector &) override; + void setResponse(std::shared_ptr response); - private: +private: std_msgs::msg::Header getHeader(); const std::string service_name_; std::shared_ptr node_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_topic_output.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_topic_output.hpp index 02650d65..8778208c 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_topic_output.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/ros_topic_output.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for RosTopicOutput Class @@ -43,19 +41,21 @@ #include "dynamic_vino_lib/inferences/face_detection.hpp" #include "dynamic_vino_lib/outputs/base_output.hpp" -namespace Outputs { +namespace Outputs +{ /** * @class RosTopicOutput * @brief This class handles and publish the detection result with ros topic. */ -class RosTopicOutput : public BaseOutput { - public: +class RosTopicOutput : public BaseOutput +{ +public: RosTopicOutput(); /** * @brief Calculate the camera matrix of a frame. * @param[in] A frame. */ - void feedFrame(const cv::Mat&) override; + void feedFrame(const cv::Mat &) override; /** * @brief Publish all the detected infomations generated by the accept * functions with ros topic. @@ -66,49 +66,46 @@ class RosTopicOutput : public BaseOutput { * the object segmentation result. * @param[in] results a bundle of object segmentation results. */ - void accept( - const std::vector& ) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the object detection result. * @param[in] results a bundle of object detection results. */ - void accept( - const std::vector& ) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the face detection result. * @param[in] An face detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the emotion detection result. * @param[in] An emotion detection result objetc. */ - void accept(const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the age gender detection result. * @param[in] An age gender detection result objetc. */ - void accept(const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate ros topic infomation according to * the headpose detection result. * @param[in] An head pose detection result objetc. */ - void accept(const std::vector&) override; + void accept(const std::vector &) override; - private: +private: std_msgs::msg::Header getHeader(); const std::string topic_name_; std::shared_ptr node_; rclcpp::Publisher::SharedPtr pub_segmented_object_; std::shared_ptr segmented_objects_topic_; rclcpp::Publisher::SharedPtr pub_detected_object_; - std::shared_ptr detected_objects_topic_; + std::shared_ptr detected_objects_topic_; rclcpp::Publisher::SharedPtr pub_face_; std::shared_ptr faces_topic_; rclcpp::Publisher::SharedPtr pub_emotion_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/rviz_output.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/rviz_output.hpp index 093c9edd..a675915f 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/outputs/rviz_output.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/outputs/rviz_output.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief A header file with declaration for RvizOutput Class @@ -30,19 +28,21 @@ #include "dynamic_vino_lib/outputs/base_output.hpp" #include "dynamic_vino_lib/outputs/image_window_output.hpp" -namespace Outputs { +namespace Outputs +{ /** * @class RvizOutput * @brief This class handles and shows the detection result with rviz. */ -class RvizOutput : public BaseOutput { - public: +class RvizOutput : public BaseOutput +{ +public: explicit RvizOutput(); /** * @brief Construct frame for rviz * @param[in] A frame. */ - void feedFrame(const cv::Mat&) override; + void feedFrame(const cv::Mat &) override; /** * @brief Show all the contents generated by the accept * functions with rviz. @@ -53,45 +53,39 @@ class RvizOutput : public BaseOutput { * the face detection result. * @param[in] A face detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate rviz output content according to * the object detection result. * @param[in] results A bundle of object detection results. */ - void accept( - const std::vector&) override; - /** - * @brief Generate rviz output content according to - * the object segmentation result. - * @param[in] results A bundle of object segmentation results. - */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; + /** + * @brief Generate rviz output content according to + * the object segmentation result. + * @param[in] results A bundle of object segmentation results. + */ + void accept(const std::vector &) override; /** * @brief Generate rviz output content according to * the emotion detection result. * @param[in] A emotion detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate rviz output content according to * the age and gender detection result. * @param[in] A head pose detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; /** * @brief Generate rviz output content according to * the headpose detection result. * @param[in] An age gender detection result objetc. */ - void accept( - const std::vector&) override; + void accept(const std::vector &) override; - private: +private: std_msgs::msg::Header getHeader(); std::shared_ptr node_; rclcpp::Publisher::SharedPtr pub_image_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline.hpp index 19699e4d..a34d4ba6 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline.hpp @@ -41,17 +41,19 @@ * the input device, output device and networks and make inference. One pipeline * should have only one input device. */ -class Pipeline { - public: - explicit Pipeline(const std::string& name = "pipeline"); +class Pipeline +{ +public: + explicit Pipeline(const std::string & name = "pipeline"); /** * @brief Add input device to the pipeline. * @param[in] name name of the current input device. * @param[in] input_device the input device instance to be added. * @return whether the add operation is successful */ - bool add(const std::string& name, - std::shared_ptr input_device); + bool add( + const std::string & name, + std::shared_ptr input_device); /** * @brief Add inference network to the pipeline. * @param[in] parent name of the parent device or inference. @@ -59,8 +61,9 @@ class Pipeline { * @param[in] inference the inference instance to be added. * @return whether the add operation is successful */ - bool add(const std::string& parent, const std::string& name, - std::shared_ptr inference); + bool add( + const std::string & parent, const std::string & name, + std::shared_ptr inference); /** * @brief Add output device to the pipeline. * @param[in] parent name of the parent inference. @@ -68,21 +71,24 @@ class Pipeline { * @param[in] output the output instance to be added. * @return whether the add operation is successful */ - bool add(const std::string& parent, const std::string& name, - std::shared_ptr output); + bool add( + const std::string & parent, const std::string & name, + std::shared_ptr output); - bool add(const std::string& name, - std::shared_ptr output); - void addConnect(const std::string& parent, const std::string& name); - bool add(const std::string& name, - std::shared_ptr inference); + bool add( + const std::string & name, + std::shared_ptr output); + void addConnect(const std::string & parent, const std::string & name); + bool add( + const std::string & name, + std::shared_ptr inference); /** * @brief Add inference network-output device edge to the pipeline. * @param[in] parent name of the parent inference. * @param[in] name name of the current output device. * @return whether the add operation is successful */ - bool add(const std::string& parent, const std::string& name); + bool add(const std::string & parent, const std::string & name); /** * @brief Do the inference once. * Data flow from input device to inference network, then to output device. @@ -93,7 +99,7 @@ class Pipeline { * pipeline. */ void runService(std::string config_path); - void callback(const std::string& detection_name); + void callback(const std::string & detection_name); /** * @brief Set the inference network to call the callback function as soon as * each inference is @@ -101,14 +107,18 @@ class Pipeline { */ void setCallback(); void printPipeline(); - std::map> getOutputHandle() { return name_to_output_map_; } ; - void setParams(PipelineParams pipeline_params) { + std::map> getOutputHandle() + { + return name_to_output_map_; + } + void setParams(PipelineParams pipeline_params) + { params_ = std::make_shared(pipeline_params); - }; - const std::shared_ptr getParameters() { return params_; }; - std::shared_ptr getInputDevice() { return input_device_; }; + } + const std::shared_ptr getParameters() {return params_;} + std::shared_ptr getInputDevice() {return input_device_;} - private: +private: void initInferenceCounter(); void increaseInferenceCounter(); void decreaseInferenceCounter(); @@ -126,9 +136,9 @@ class Pipeline { std::string input_device_name_; std::multimap next_; std::map> - name_to_detection_map_; + name_to_detection_map_; std::map> - name_to_output_map_; + name_to_output_map_; int total_inference_ = 0; std::set output_names_; int width_ = 0; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_manager.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_manager.hpp index d8aa6e31..97ebced8 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_manager.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_manager.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Pipeline Manager class @@ -36,37 +34,39 @@ * @class PipelineManager * @brief This class manages the lifecycles of pipelines. */ -class PipelineManager { - public: +class PipelineManager +{ +public: /** * @brief Get the singleton instance of PipelineManager class. * The instance will be created when first call. * @return The reference of PipelineManager instance. */ - static PipelineManager& getInstance() { + static PipelineManager& getInstance() + { static PipelineManager manager_; return manager_; - }; + } - std::shared_ptr createPipeline( - const Params::ParamManager::PipelineParams& params); + std::shared_ptr createPipeline(const Params::ParamManager::PipelineParams& params); void removePipeline(const std::string& name); - PipelineManager& updatePipeline( - const std::string& name, - const Params::ParamManager::PipelineParams& params); + PipelineManager& updatePipeline(const std::string& name, + const Params::ParamManager::PipelineParams& params); void runAll(); void stopAll(); void joinAll(); - - enum PipelineState { + + enum PipelineState + { PipelineState_ThreadNotCreated, PipelineState_ThreadStopped, PipelineState_ThreadRunning, PipelineState_Error }; - struct PipelineData { + struct PipelineData + { Params::ParamManager::PipelineParams params; std::shared_ptr pipeline; std::vector> spin_nodes; @@ -74,31 +74,36 @@ class PipelineManager { PipelineState state; }; - std::map getPipelines() { return pipelines_; }; + std::map getPipelines() + { + return pipelines_; + } - private: - PipelineManager(){}; +private: + PipelineManager() + { + } PipelineManager(PipelineManager const&); void operator=(PipelineManager const&); void threadPipeline(const char* name); std::map> parseInputDevice(const Params::ParamManager::PipelineParams& params); - std::map> parseOutput( - const Params::ParamManager::PipelineParams& params); + std::map> + parseOutput(const Params::ParamManager::PipelineParams& params); std::map> parseInference(const Params::ParamManager::PipelineParams& params); - std::shared_ptr createFaceDetection( - const Params::ParamManager::InferenceParams& infer); - std::shared_ptr createAgeGenderRecognition( - const Params::ParamManager::InferenceParams& infer); - std::shared_ptr createEmotionRecognition( - const Params::ParamManager::InferenceParams& infer); - std::shared_ptr createHeadPoseEstimation( - const Params::ParamManager::InferenceParams& infer); - std::shared_ptr createObjectDetection( - const Params::ParamManager::InferenceParams& infer); - std::shared_ptr createObjectSegmentation( - const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createFaceDetection(const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createAgeGenderRecognition(const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createEmotionRecognition(const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createHeadPoseEstimation(const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createObjectDetection(const Params::ParamManager::InferenceParams& infer); + std::shared_ptr + createObjectSegmentation(const Params::ParamManager::InferenceParams& infer); std::map pipelines_; std::map plugins_for_devices_; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_params.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_params.hpp index 5dfacf6c..9c38e898 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_params.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/pipeline_params.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with declaration of Pipeline class @@ -59,19 +57,19 @@ extern const std::string kInferTpye_ObjectSegmentation; * @brief This class is a pipeline parameter management that stores parameters * of a given pipeline */ -class PipelineParams { - public: +class PipelineParams +{ +public: PipelineParams(const std::string& name); PipelineParams(const Params::ParamManager::PipelineParams& params); - static Params::ParamManager::PipelineParams getPipeline( - const std::string& name); + static Params::ParamManager::PipelineParams getPipeline(const std::string& name); PipelineParams& operator=(const Params::ParamManager::PipelineParams& params); void update(); void update(const Params::ParamManager::PipelineParams& params); bool isOutputTo(std::string& name); bool isGetFps(); - private: +private: Params::ParamManager::PipelineParams params_; }; diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/services/frame_processing_server.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/services/frame_processing_server.hpp index e7ffeedb..70ecdc31 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/services/frame_processing_server.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/services/frame_processing_server.hpp @@ -1,31 +1,29 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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 DYNAMIC_VINO_LIB__FRAME_PROCESSING_SERVER_HPP_ #define DYNAMIC_VINO_LIB__FRAME_PROCESSING_SERVER_HPP_ #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -35,27 +33,33 @@ #include -namespace vino_service { - -class FrameProcessingServer : public rclcpp::Node { +namespace vino_service +{ +class FrameProcessingServer : public rclcpp::Node +{ public: explicit FrameProcessingServer(const std::string service_name, const std::string config_path); private: - void cbFaceDetection(const std::shared_ptr request, - std::shared_ptr response); + void cbFaceDetection( + const std::shared_ptr request, + std::shared_ptr response); - void cbAgeGenderRecognition(const std::shared_ptr request, - std::shared_ptr response); + void cbAgeGenderRecognition( + const std::shared_ptr request, + std::shared_ptr response); - void cbEmotionRecognition(const std::shared_ptr request, - std::shared_ptr response); + void cbEmotionRecognition( + const std::shared_ptr request, + std::shared_ptr response); - void cbHeadPoseRecognition(const std::shared_ptr request, - std::shared_ptr response); + void cbHeadPoseRecognition( + const std::shared_ptr request, + std::shared_ptr response); - void cbObjectDetection(const std::shared_ptr request, - std::shared_ptr response); + void cbObjectDetection( + const std::shared_ptr request, + std::shared_ptr response); rclcpp::Service::SharedPtr face_service_; rclcpp::Service::SharedPtr age_gender_service_; @@ -63,5 +67,5 @@ class FrameProcessingServer : public rclcpp::Node { rclcpp::Service::SharedPtr head_pose_service_; rclcpp::Service::SharedPtr object_service_; }; -} //namespace frame_processing_service +} // namespace frame_processing_service #endif // DYNAMIC_VINO_LIB__FRAME_PROCESSING_SERVER_HPP_ diff --git a/dynamic_vino_lib/include/dynamic_vino_lib/slog.hpp b/dynamic_vino_lib/include/dynamic_vino_lib/slog.hpp index 84381342..9c36de46 100644 --- a/dynamic_vino_lib/include/dynamic_vino_lib/slog.hpp +++ b/dynamic_vino_lib/include/dynamic_vino_lib/slog.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with logging facility for common samples @@ -26,13 +24,16 @@ #include #include -namespace slog { +namespace slog +{ /** * @class LogStreamEndLine * @brief The LogStreamEndLine class implements an end line marker for a log * stream */ -class LogStreamEndLine {}; +class LogStreamEndLine +{ +}; static constexpr LogStreamEndLine endl; @@ -40,18 +41,19 @@ static constexpr LogStreamEndLine endl; * @class LogStream * @brief The LogStream class implements a stream for sample logging */ -class LogStream { +class LogStream +{ std::string _prefix; std::ostream* _log_stream; bool _new_line; - public: +public: /** * @brief A constructor. Creates an LogStream object * @param prefix The prefix to print */ - LogStream(const std::string& prefix, std::ostream& log_stream) - : _prefix(prefix), _new_line(true) { + LogStream(const std::string& prefix, std::ostream& log_stream) : _prefix(prefix), _new_line(true) + { _log_stream = &log_stream; } @@ -60,8 +62,10 @@ class LogStream { * @param arg Object for serialization in the logger message */ template - LogStream& operator<<(const T& arg) { - if (_new_line) { + LogStream& operator<<(const T& arg) + { + if (_new_line) + { (*_log_stream) << "[ " << _prefix << " ] "; _new_line = false; } @@ -71,7 +75,8 @@ class LogStream { } // Specializing for LogStreamEndLine to support slog::endl - LogStream& operator<<(const LogStreamEndLine& arg) { + LogStream& operator<<(const LogStreamEndLine& arg) + { _new_line = true; (*_log_stream) << std::endl; diff --git a/sample/include/utility.hpp b/sample/include/utility.hpp index 154931e7..9aa1da65 100644 --- a/sample/include/utility.hpp +++ b/sample/include/utility.hpp @@ -1,20 +1,19 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ - -#pragma once +// Copyright (c) 2018 Intel Corporation +// +// 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 UTILITY_HPP_ +#define UTILITY_HPP_ #include @@ -33,83 +32,79 @@ static const char help_message[] = "Print a usage message."; /// @brief message for images argument static const char input_choice[] = - "Optional. Input choice (RealSenseCamera, StandardCamera, Video, Image). " \ - "Default value is StandardCamera."; + "Optional. Input choice (RealSenseCamera, StandardCamera, Video, Image). " + "Default value is StandardCamera."; /// @brief message for model argument -static const char face_detection_model_message[] = - "Required. Path to an .xml file with a trained face detection model."; -static const char age_gender_model_message[] = - "Optional. Path to an .xml file with a trained age gender model."; -static const char head_pose_model_message[] = - "Optional. Path to an .xml file with a trained head pose model."; -static const char emotions_model_message[] = - "Optional. Path to an .xml file with a trained emotions model."; +static const char face_detection_model_message[] = "Required. Path to an .xml file with a trained " + "face detection model."; +static const char age_gender_model_message[] = "Optional. Path to an .xml file with a trained age " + "gender model."; +static const char head_pose_model_message[] = "Optional. Path to an .xml file with a trained head " + "pose model."; +static const char emotions_model_message[] = "Optional. Path to an .xml file with a trained " + "emotions model."; static const char object_model_message[] = "Required. Path to an .xml file with a trained model."; /// @brief message for plugin argument static const char plugin_message[] = - "Plugin name. For example MKLDNNPlugin. If this parameter is pointed, " \ -"the sample will look for this plugin only."; + "Plugin name. For example MKLDNNPlugin. If this parameter is pointed, " + "the sample will look for this plugin only."; /// @brief message for assigning face detection calculation to device static const char target_device_message[] = - "Specify the target device for Face Detection (CPU, GPU, FPGA, or MYRIAD). " \ -"Sample will look for a suitable plugin for device specified."; + "Specify the target device for Face Detection (CPU, GPU, FPGA, or MYRIAD). " + "Sample will look for a suitable plugin for device specified."; /// @brief message for assigning age gender calculation to device static const char target_device_message_ag[] = - "Specify the target device for Age Gender Detection (CPU, GPU, FPGA, or MYRIAD). " \ -"Sample will look for a suitable plugin for device specified."; + "Specify the target device for Age Gender Detection (CPU, GPU, FPGA, or MYRIAD). " + "Sample will look for a suitable plugin for device specified."; /// @brief message for assigning age gender calculation to device static const char target_device_message_hp[] = - "Specify the target device for Head Pose Detection (CPU, GPU, FPGA, or MYRIAD). " \ -"Sample will look for a suitable plugin for device specified."; + "Specify the target device for Head Pose Detection (CPU, GPU, FPGA, or MYRIAD). " + "Sample will look for a suitable plugin for device specified."; static const char target_device_message_em[] = - "Specify the target device for Emotions Detection (CPU, GPU, FPGA, or MYRIAD). " \ -"Sample will look for a suitable plugin for device specified."; + "Specify the target device for Emotions Detection (CPU, GPU, FPGA, or MYRIAD). " + "Sample will look for a suitable plugin for device specified."; /// @brief message for number of simultaneously age gender detections using dynamic batch static const char num_batch_ag_message[] = - "Specify number of maximum simultaneously processed faces for Age Gender Detection" \ - "(default is 16)."; + "Specify number of maximum simultaneously processed faces for Age Gender Detection" + "(default is 16)."; /// @brief message for number of simultaneously age gender detections using dynamic batch static const char num_batch_hp_message[] = - "Specify number of maximum simultaneously processed faces for Head Pose Detection" \ - "(default is 16)."; + "Specify number of maximum simultaneously processed faces for Head Pose Detection" + "(default is 16)."; /// @brief message for number of simultaneously age gender detections using dynamic batch static const char num_batch_em_message[] = - "Specify number of maximum simultaneously processed faces for Emotions Detection" \ - "(default is 16)."; + "Specify number of maximum simultaneously processed faces for Emotions Detection" + "(default is 16)."; /// @brief message for performance counters -static const char - performance_counter_message[] = "Enables per-layer performance report."; +static const char performance_counter_message[] = "Enables per-layer performance report."; /// @brief message for clDNN custom kernels desc -static const char - custom_cldnn_message[] = "Required for clDNN (GPU)-targeted custom kernels."\ -"Absolute path to the xml file with the kernels desc."; +static const char custom_cldnn_message[] = "Required for clDNN (GPU)-targeted custom kernels." + "Absolute path to the xml file with the kernels desc."; /// @brief message for user library argument static const char custom_cpu_library_message[] = - "Required for MKLDNN (CPU)-targeted custom layers." \ -"Absolute path to a shared library with the kernels impl."; + "Required for MKLDNN (CPU)-targeted custom layers." + "Absolute path to a shared library with the kernels impl."; /// @brief message for probability threshold argument -static const char - thresh_output_message[] = "Probability threshold for detections."; +static const char thresh_output_message[] = "Probability threshold for detections."; /// @brief message raw output flag static const char raw_output_message[] = "Inference results as raw values."; /// @brief message no wait for keypress after input stream completed -static const char - no_wait_for_keypress_message[] = "No wait for key press in the end."; +static const char no_wait_for_keypress_message[] = "No wait for key press in the end."; /// @brief message no show processed video static const char no_show_processed_video[] = "No show processed video."; @@ -202,7 +197,8 @@ DEFINE_string(config, "", parameter_file_message); /** * \brief This function show a help message */ -static void showUsage() { +static void showUsage() +{ std::cout << std::endl; std::cout << "interactive_face_detection [OPTION]" << std::endl; std::cout << "Options:" << std::endl; @@ -210,60 +206,42 @@ static void showUsage() { std::cout << " -h " << help_message << std::endl; std::cout << " -i " << input_choice << std::endl; std::cout << " -i_path " << input_file_path << std::endl; - std::cout << " -m \"\" " - << face_detection_model_message << std::endl; - std::cout << " -m_ag \"\" " << age_gender_model_message - << std::endl; - std::cout << " -m_hp \"\" " << head_pose_model_message - << std::endl; - std::cout << " -m_em \"\" " << emotions_model_message - << std::endl; - std::cout << " -l \"\" " << custom_cpu_library_message - << std::endl; + std::cout << " -m \"\" " << face_detection_model_message << std::endl; + std::cout << " -m_ag \"\" " << age_gender_model_message << std::endl; + std::cout << " -m_hp \"\" " << head_pose_model_message << std::endl; + std::cout << " -m_em \"\" " << emotions_model_message << std::endl; + std::cout << " -l \"\" " << custom_cpu_library_message << std::endl; std::cout << " Or" << std::endl; - std::cout << " -c \"\" " << custom_cldnn_message - << std::endl; - std::cout << " -d \"\" " << target_device_message - << std::endl; - std::cout << " -d_ag \"\" " << target_device_message_ag - << std::endl; - std::cout << " -d_hp \"\" " << target_device_message_hp - << std::endl; - std::cout << " -d_em \"\" " << target_device_message_em - << std::endl; - std::cout << " -n_ag \"\" " << num_batch_ag_message - << std::endl; - std::cout << " -n_hp \"\" " << num_batch_hp_message - << std::endl; - std::cout << " -n_em \"\" " << num_batch_em_message - << std::endl; - std::cout << " -no_wait " << no_wait_for_keypress_message - << std::endl; - std::cout << " -no_show " << no_show_processed_video - << std::endl; - std::cout << " -pc " << performance_counter_message - << std::endl; - std::cout << " -r " << raw_output_message - << std::endl; - std::cout << " -t " << thresh_output_message - << std::endl; + std::cout << " -c \"\" " << custom_cldnn_message << std::endl; + std::cout << " -d \"\" " << target_device_message << std::endl; + std::cout << " -d_ag \"\" " << target_device_message_ag << std::endl; + std::cout << " -d_hp \"\" " << target_device_message_hp << std::endl; + std::cout << " -d_em \"\" " << target_device_message_em << std::endl; + std::cout << " -n_ag \"\" " << num_batch_ag_message << std::endl; + std::cout << " -n_hp \"\" " << num_batch_hp_message << std::endl; + std::cout << " -n_em \"\" " << num_batch_em_message << std::endl; + std::cout << " -no_wait " << no_wait_for_keypress_message << std::endl; + std::cout << " -no_show " << no_show_processed_video << std::endl; + std::cout << " -pc " << performance_counter_message << std::endl; + std::cout << " -r " << raw_output_message << std::endl; + std::cout << " -t " << thresh_output_message << std::endl; } -static void showUsageForParam() { +static void showUsageForParam() +{ std::cout << std::endl; std::cout << "vino_param_sample [OPTION]" << std::endl; std::cout << "Options:" << std::endl; std::cout << std::endl; std::cout << " -h " << help_message << std::endl; - std::cout << " -config \"\" " << parameter_file_message - << std::endl; + std::cout << " -config \"\" " << parameter_file_message << std::endl; } - /** * \brief This function show a help message */ -static void showUsageForObjectDetection() { +static void showUsageForObjectDetection() +{ std::cout << std::endl; std::cout << "async_object_detection_ssd [OPTION]" << std::endl; std::cout << "Options:" << std::endl; @@ -278,4 +256,6 @@ static void showUsageForObjectDetection() { std::cout << " -pc " << performance_counter_message << std::endl; std::cout << " -r " << raw_output_message << std::endl; std::cout << " -t " << thresh_output_message << std::endl; -} \ No newline at end of file +} + +#endif // UTILITY_HPP_ diff --git a/vino_param_lib/include/vino_param_lib/param_manager.hpp b/vino_param_lib/include/vino_param_lib/param_manager.hpp index 02971039..7ff18c14 100644 --- a/vino_param_lib/include/vino_param_lib/param_manager.hpp +++ b/vino_param_lib/include/vino_param_lib/param_manager.hpp @@ -1,18 +1,17 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. + /** * @brief A header file with declaration for parameter management * @file param_manager.hpp @@ -25,7 +24,8 @@ #include #include -namespace Params { +namespace Params +{ /** * @class ParamManager * @brief This class implements yaml-based parameter management. @@ -40,13 +40,14 @@ namespace Params { */ class ParamManager // singleton { - public: +public: /** * @brief Get the singleton instance of ParamManager class. * The instance will be created when first call. * @return The reference of paramManager class. */ - static ParamManager& getInstance() { + static ParamManager& getInstance() + { static ParamManager manager_; return manager_; } @@ -57,13 +58,15 @@ class ParamManager // singleton */ void print() const; - struct InferenceParams { + struct InferenceParams + { std::string name; std::string engine; std::string model; std::string label; }; - struct PipelineParams { + struct PipelineParams + { std::string name; std::vector infers; std::vector inputs; @@ -71,7 +74,8 @@ class ParamManager // singleton std::multimap connects; std::string input_meta; }; - struct CommonParams { + struct CommonParams + { std::string custom_cpu_library; std::string custom_cldnn_library; bool enable_performance_count = false; @@ -96,7 +100,10 @@ class ParamManager // singleton * @brief Retrieve pipeline parameters. * @return A list of struct PipelineParams storing all pipeline parameters. */ - std::vector getPipelines() const { return pipelines_; } + std::vector getPipelines() const + { + return pipelines_; + } /** * @brief Retrieve the specific pipeline parameters by the given pipeline @@ -110,10 +117,15 @@ class ParamManager // singleton * @brief Retrieve common parameters. * @return struct CommonParams storing all common parameters. */ - CommonParams getCommon() const { return common_; } + CommonParams getCommon() const + { + return common_; + } - private: - ParamManager() {} +private: + ParamManager() + { + } ParamManager(ParamManager const&); void operator=(ParamManager const&); diff --git a/vino_param_lib/include/vino_param_lib/slog.hpp b/vino_param_lib/include/vino_param_lib/slog.hpp index 84381342..9c36de46 100644 --- a/vino_param_lib/include/vino_param_lib/slog.hpp +++ b/vino_param_lib/include/vino_param_lib/slog.hpp @@ -1,18 +1,16 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * 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. - */ +// Copyright (c) 2018 Intel Corporation +// +// 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. /** * @brief a header file with logging facility for common samples @@ -26,13 +24,16 @@ #include #include -namespace slog { +namespace slog +{ /** * @class LogStreamEndLine * @brief The LogStreamEndLine class implements an end line marker for a log * stream */ -class LogStreamEndLine {}; +class LogStreamEndLine +{ +}; static constexpr LogStreamEndLine endl; @@ -40,18 +41,19 @@ static constexpr LogStreamEndLine endl; * @class LogStream * @brief The LogStream class implements a stream for sample logging */ -class LogStream { +class LogStream +{ std::string _prefix; std::ostream* _log_stream; bool _new_line; - public: +public: /** * @brief A constructor. Creates an LogStream object * @param prefix The prefix to print */ - LogStream(const std::string& prefix, std::ostream& log_stream) - : _prefix(prefix), _new_line(true) { + LogStream(const std::string& prefix, std::ostream& log_stream) : _prefix(prefix), _new_line(true) + { _log_stream = &log_stream; } @@ -60,8 +62,10 @@ class LogStream { * @param arg Object for serialization in the logger message */ template - LogStream& operator<<(const T& arg) { - if (_new_line) { + LogStream& operator<<(const T& arg) + { + if (_new_line) + { (*_log_stream) << "[ " << _prefix << " ] "; _new_line = false; } @@ -71,7 +75,8 @@ class LogStream { } // Specializing for LogStreamEndLine to support slog::endl - LogStream& operator<<(const LogStreamEndLine& arg) { + LogStream& operator<<(const LogStreamEndLine& arg) + { _new_line = true; (*_log_stream) << std::endl; From 8a14d7a4912423a7e217722497113af284ec82fb Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:19:26 -0500 Subject: [PATCH 04/16] code style reformat --- sample/launch/pipeline_image.launch.py | 13 ++++++++----- sample/launch/pipeline_image_oss.launch.py | 13 ++++++++----- sample/launch/pipeline_object.launch.py | 10 ++++++---- sample/launch/pipeline_object_oss.launch.py | 10 ++++++---- .../launch/pipeline_object_oss_topic.launch.py | 9 ++++++--- sample/launch/pipeline_object_topic.launch.py | 15 +++++++++------ sample/launch/pipeline_people_myriad.launch.py | 13 ++++++++----- sample/launch/pipeline_people_oss.launch.py | 13 ++++++++----- sample/launch/pipeline_segmentation.launch.py | 18 +++++++++++------- sample/launch/pipeline_video.launch.py | 13 ++++++++----- 10 files changed, 78 insertions(+), 49 deletions(-) diff --git a/sample/launch/pipeline_image.launch.py b/sample/launch/pipeline_image.launch.py index 52dfaf02..27f9bdec 100644 --- a/sample/launch/pipeline_image.launch.py +++ b/sample/launch/pipeline_image.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_image.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_image.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -33,11 +35,12 @@ def generate_launch_description(): ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), - ('/openvino_toolkit/age_genders', '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_image_oss.launch.py b/sample/launch/pipeline_image_oss.launch.py index 7ca5cd87..f4bdb383 100644 --- a/sample/launch/pipeline_image_oss.launch.py +++ b/sample/launch/pipeline_image_oss.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_image_oss.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_image_oss.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -33,11 +35,12 @@ def generate_launch_description(): ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), - ('/openvino_toolkit/age_genders', '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_object.launch.py b/sample/launch/pipeline_object.launch.py index b8e3f43d..52c5a466 100644 --- a/sample/launch/pipeline_object.launch.py +++ b/sample/launch/pipeline_object.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_object.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_object.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -34,7 +36,7 @@ def generate_launch_description(): ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_object_oss.launch.py b/sample/launch/pipeline_object_oss.launch.py index 8519c298..509b4113 100644 --- a/sample/launch/pipeline_object_oss.launch.py +++ b/sample/launch/pipeline_object_oss.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_object_oss.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_object_oss.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -34,7 +36,7 @@ def generate_launch_description(): ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_object_oss_topic.launch.py b/sample/launch/pipeline_object_oss_topic.launch.py index 8158da7c..47557a31 100644 --- a/sample/launch/pipeline_object_oss_topic.launch.py +++ b/sample/launch/pipeline_object_oss_topic.launch.py @@ -22,12 +22,15 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_object_oss_topic.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_object_oss_topic.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ # Realsense launch_ros.actions.Node( - package='realsense_ros2_camera', node_executable='realsense_ros2_camera',output='screen'), + package='realsense_ros2_camera', node_executable='realsense_ros2_camera', + output='screen'), # Openvino Detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', diff --git a/sample/launch/pipeline_object_topic.launch.py b/sample/launch/pipeline_object_topic.launch.py index 0da02d1d..cd8e9889 100644 --- a/sample/launch/pipeline_object_topic.launch.py +++ b/sample/launch/pipeline_object_topic.launch.py @@ -22,14 +22,17 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_object_topic.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_object_topic.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #realsense + # Realsense launch_ros.actions.Node( - package='realsense_ros2_camera', node_executable='realsense_ros2_camera',output='screen'), + package='realsense_ros2_camera', node_executable='realsense_ros2_camera', + output='screen'), - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -39,7 +42,7 @@ def generate_launch_description(): ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_people_myriad.launch.py b/sample/launch/pipeline_people_myriad.launch.py index 00b0e790..ad0ed619 100644 --- a/sample/launch/pipeline_people_myriad.launch.py +++ b/sample/launch/pipeline_people_myriad.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_people_myriad.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_people_myriad.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -33,11 +35,12 @@ def generate_launch_description(): ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), - ('/openvino_toolkit/age_genders', '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_people_oss.launch.py b/sample/launch/pipeline_people_oss.launch.py index 6f8b3d9d..79afc1ec 100644 --- a/sample/launch/pipeline_people_oss.launch.py +++ b/sample/launch/pipeline_people_oss.launch.py @@ -22,10 +22,12 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_people_oss.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_people_oss.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], @@ -33,11 +35,12 @@ def generate_launch_description(): ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), - ('/openvino_toolkit/age_genders', '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_segmentation.launch.py b/sample/launch/pipeline_segmentation.launch.py index a779a664..9aaddc38 100644 --- a/sample/launch/pipeline_segmentation.launch.py +++ b/sample/launch/pipeline_segmentation.launch.py @@ -22,24 +22,28 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_segmentation.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_segmentation.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #realsense + # Realsense launch_ros.actions.Node( - package='realsense_ros2_camera', node_executable='realsense_ros2_camera',output='screen'), + package='realsense_ros2_camera', node_executable='realsense_ros2_camera', + output='screen'), - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], remappings=[ ('/openvino_toolkit/image_raw', '/camera/color/image_raw'), - ('/openvino_toolkit/segmented_obejcts', '/ros2_openvino_toolkit/segmented_obejcts'), + ('/openvino_toolkit/segmented_obejcts', + '/ros2_openvino_toolkit/segmented_obejcts'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), diff --git a/sample/launch/pipeline_video.launch.py b/sample/launch/pipeline_video.launch.py index e031693e..5db5ccec 100644 --- a/sample/launch/pipeline_video.launch.py +++ b/sample/launch/pipeline_video.launch.py @@ -22,19 +22,22 @@ def generate_launch_description(): - default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', 'pipeline_video.yaml'); - default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', 'rviz/default.rviz'); + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'pipeline_video.yaml') + default_rviz = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'launch', + 'rviz/default.rviz') return LaunchDescription([ - #openvino detection + # Openvino detection launch_ros.actions.Node( package='dynamic_vino_sample', node_executable='pipeline_with_params', arguments=['-config', default_yaml], remappings=[ - ('/openvino_toolkit/segmented_obejcts', '/ros2_openvino_toolkit/segmented_obejcts'), + ('/openvino_toolkit/segmented_obejcts', + '/ros2_openvino_toolkit/segmented_obejcts'), ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], output='screen'), - #rviz + # Rviz launch_ros.actions.Node( package='rviz2', node_executable='rviz2', output='screen', arguments=['--display-config', default_rviz]), From d421d4774c9c3634b1ad881149eb5d0ad88b195c Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:20:06 -0500 Subject: [PATCH 05/16] modify the path of the model --- sample/param/image_object_server.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/param/image_object_server.yaml b/sample/param/image_object_server.yaml index aacd8e67..ace5d1c6 100644 --- a/sample/param/image_object_server.yaml +++ b/sample/param/image_object_server.yaml @@ -3,7 +3,7 @@ Pipelines: inputs: [Image] infers: - name: ObjectDetection - model: /opt/intel/computer_vision_sdk/deployment_tools/intel_models/person-vehicle-bike-detection-crossroad-0078/FP32/person-vehicle-bike-detection-crossroad-0078.xml + model: /opt/intel/computer_vision_sdk/deployment_tools/model_downloader/object_detection/common/ssd/300/caffe/output/ssd300.xml engine: CPU label: to/be/set/xxx.labels batch: 16 From 053d94638a89be9c2dea9441cc79b40d1ba71b0f Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:26:07 -0500 Subject: [PATCH 06/16] add openvino unit test --- .../lib/unittest_createPipelineCheck.cpp | 73 +++++++++ .../tests/service/unittest_objectService.cpp | 86 +++++++++++ .../topic/unittest_faceDetectionCheck.cpp | 138 ++++++++++++++++++ sample/tests/topic/unittest_imageCheck.cpp | 138 ++++++++++++++++++ .../topic/unittest_objectDetectionCheck.cpp | 104 +++++++++++++ .../topic/unittest_segmentationCheck.cpp | 105 +++++++++++++ 6 files changed, 644 insertions(+) create mode 100644 sample/tests/lib/unittest_createPipelineCheck.cpp create mode 100644 sample/tests/service/unittest_objectService.cpp create mode 100644 sample/tests/topic/unittest_faceDetectionCheck.cpp create mode 100644 sample/tests/topic/unittest_imageCheck.cpp create mode 100644 sample/tests/topic/unittest_objectDetectionCheck.cpp create mode 100644 sample/tests/topic/unittest_segmentationCheck.cpp diff --git a/sample/tests/lib/unittest_createPipelineCheck.cpp b/sample/tests/lib/unittest_createPipelineCheck.cpp new file mode 100644 index 00000000..efe0a64c --- /dev/null +++ b/sample/tests/lib/unittest_createPipelineCheck.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/pipeline.hpp" +#include "dynamic_vino_lib/pipeline_manager.hpp" +#include "dynamic_vino_lib/slog.hpp" +#include "extension/ext_list.hpp" +#include "gflags/gflags.h" +#include "inference_engine.hpp" +#include "librealsense2/rs.hpp" +#include "opencv2/opencv.hpp" +#include "utility.hpp" + +std::string getConfigPath() +{ + std::string content; + std::string prefix_path; + ament_index_cpp::get_resource("packages", "dynamic_vino_sample", content, &prefix_path); + return prefix_path + "/share/dynamic_vino_sample/testParam/pipeline_face_test.yaml"; +} + +TEST(UnitTestCheckPipeline, testPipeline) +{ + std::string config_file = getConfigPath(); + EXPECT_TRUE(std::ifstream(config_file).is_open()); + ASSERT_NO_THROW({ + Params::ParamManager::getInstance().parse(config_file); + auto pipelines = Params::ParamManager::getInstance().getPipelines(); + EXPECT_GT(pipelines.size(), 0); + + for (auto & p : pipelines) { + PipelineManager::getInstance().createPipeline(p); + } + }); +} + +int main(int argc, char * argv[]) +{ + testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/sample/tests/service/unittest_objectService.cpp b/sample/tests/service/unittest_objectService.cpp new file mode 100644 index 00000000..e020e1f2 --- /dev/null +++ b/sample/tests/service/unittest_objectService.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2017 Intel Corporation. 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/services/frame_processing_server.hpp" + +std::string generate_file_path(std::string path) +{ + std::string base_path = __FILE__; + const std::string filename = "sample/tests/service/unittest_objectService.cpp"; + base_path = base_path.substr(0, base_path.length() - filename.length() - 1); + return base_path + "/" + path; +} + +TEST(UnitTestObject, testObject) +{ + auto node = rclcpp::Node::make_shared("openvino_object_service_test"); + + auto client = node->create_client("detect_object"); + auto request = std::make_shared(); + + std::string buffer = generate_file_path("data/images/car_vihecle.png"); + std::cout << buffer << std::endl; + request->image_path = buffer; + + if (!client->wait_for_service(std::chrono::seconds(20))) { + ASSERT_TRUE(false) << "service not available after waiting"; + } + + auto result = client->async_send_request(request); + + auto ret = rclcpp::spin_until_future_complete(node, result, std::chrono::seconds(5)); + EXPECT_EQ(ret, rclcpp::executor::FutureReturnCode::SUCCESS); + + auto srv = result.get(); + + EXPECT_TRUE(srv->objects.objects_vector.size()); + + for (unsigned int i = 0; i < srv->objects.objects_vector.size(); i++) { + EXPECT_EQ(srv->objects.objects_vector[i].object.object_name, "Car"); + } + + EXPECT_TRUE(srv->objects.objects_vector[0].roi.x_offset > 1100 && + srv->objects.objects_vector[0].roi.x_offset < 1795 && + srv->objects.objects_vector[0].roi.y_offset > 215 && + srv->objects.objects_vector[0].roi.y_offset < 480); + EXPECT_TRUE(srv->objects.objects_vector[1].roi.x_offset > 310 && + srv->objects.objects_vector[1].roi.x_offset < 785 && + srv->objects.objects_vector[1].roi.y_offset > 225 && + srv->objects.objects_vector[1].roi.y_offset < 460); + EXPECT_TRUE(srv->objects.objects_vector[2].roi.x_offset > 195 && + srv->objects.objects_vector[2].roi.x_offset < 405 && + srv->objects.objects_vector[2].roi.y_offset > 220 && + srv->objects.objects_vector[2].roi.y_offset < 345); +} + +int main(int argc, char ** argv) +{ + rclcpp::init(argc, argv); + testing::InitGoogleTest(&argc, argv); + system("ros2 launch dynamic_vino_sample image_object_service_test.launch.py &"); + int ret = RUN_ALL_TESTS(); + system("killall -s SIGINT image_object_server &"); + rclcpp::shutdown(); + return ret; +} diff --git a/sample/tests/topic/unittest_faceDetectionCheck.cpp b/sample/tests/topic/unittest_faceDetectionCheck.cpp new file mode 100644 index 00000000..b75bafed --- /dev/null +++ b/sample/tests/topic/unittest_faceDetectionCheck.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/pipeline.hpp" +#include "dynamic_vino_lib/pipeline_manager.hpp" +#include "dynamic_vino_lib/slog.hpp" +#include "extension/ext_list.hpp" +#include "gflags/gflags.h" +#include "inference_engine.hpp" +#include "librealsense2/rs.hpp" +#include "opencv2/opencv.hpp" +#include "utility.hpp" +static bool test_pass = false; + +template +void wait_for_future( + rclcpp::executor::Executor & executor, std::shared_future & future, + const DurationT & timeout) +{ + using rclcpp::executor::FutureReturnCode; + rclcpp::executor::FutureReturnCode future_ret; + auto start_time = std::chrono::steady_clock::now(); + future_ret = executor.spin_until_future_complete(future, timeout); + auto elapsed_time = std::chrono::steady_clock::now() - start_time; + EXPECT_EQ(FutureReturnCode::SUCCESS, future_ret) << + "the usb camera don't publish data to topic\n" << + "future failed to be set after: " << + std::chrono::duration_cast(elapsed_time).count() << + " milliseconds\n"; +} + +TEST(UnitTestFaceDetection, testFaceDetection) +{ + auto node = rclcpp::Node::make_shared("openvino_face_test"); + rmw_qos_profile_t custom_qos_profile = rmw_qos_profile_default; + custom_qos_profile.depth = 16; + std::promise sub_called; + std::shared_future sub_called_future(sub_called.get_future()); + + auto openvino_faceDetection_callback = + [&sub_called](const object_msgs::msg::ObjectsInBoxes::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_emotionRecognition_callback = + [&sub_called](const people_msgs::msg::EmotionsStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_ageGender_callback = + [&sub_called](const people_msgs::msg::AgeGenderStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_headPose_callback = + [&sub_called](const people_msgs::msg::HeadPoseStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + rclcpp::executors::SingleThreadedExecutor executor; + executor.add_node(node); + + { + auto sub1 = node->create_subscription( + "/openvino_toolkit/detected_objects", openvino_faceDetection_callback, custom_qos_profile); + + auto sub2 = node->create_subscription( + "/ros2_openvino_toolkit/emotions_recognition", openvino_emotionRecognition_callback, + custom_qos_profile); + + auto sub3 = node->create_subscription( + "/ros2_openvino_toolkit/age_genders_Recognition", openvino_ageGender_callback, + custom_qos_profile); + + auto sub4 = node->create_subscription( + "/ros2_openvino_toolkit/headposes_estimation", openvino_headPose_callback, + custom_qos_profile); + + executor.spin_once(std::chrono::seconds(0)); + + wait_for_future(executor, sub_called_future, std::chrono::seconds(10)); + + EXPECT_TRUE(test_pass); + } +} + +int main(int argc, char * argv[]) +{ + testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + auto offset = std::chrono::seconds(30); + system("ros2 launch dynamic_vino_sample pipeline_face_test.launch.py &"); + int ret = RUN_ALL_TESTS(); + rclcpp::sleep_for(offset); + system("killall -s SIGINT pipeline_with_params &"); + rclcpp::shutdown(); + return ret; +} diff --git a/sample/tests/topic/unittest_imageCheck.cpp b/sample/tests/topic/unittest_imageCheck.cpp new file mode 100644 index 00000000..9dba76ad --- /dev/null +++ b/sample/tests/topic/unittest_imageCheck.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/pipeline.hpp" +#include "dynamic_vino_lib/pipeline_manager.hpp" +#include "dynamic_vino_lib/slog.hpp" +#include "extension/ext_list.hpp" +#include "gflags/gflags.h" +#include "inference_engine.hpp" +#include "librealsense2/rs.hpp" +#include "opencv2/opencv.hpp" +#include "utility.hpp" +static bool test_pass = false; + +template +void wait_for_future( + rclcpp::executor::Executor & executor, std::shared_future & future, + const DurationT & timeout) +{ + using rclcpp::executor::FutureReturnCode; + rclcpp::executor::FutureReturnCode future_ret; + auto start_time = std::chrono::steady_clock::now(); + future_ret = executor.spin_until_future_complete(future, timeout); + auto elapsed_time = std::chrono::steady_clock::now() - start_time; + EXPECT_EQ(FutureReturnCode::SUCCESS, future_ret) << + "the usb camera don't publish data to topic\n" << + "future failed to be set after: " << + std::chrono::duration_cast(elapsed_time).count() << + " milliseconds\n"; +} + +TEST(UnitTestImageDetection, testImageDetection) +{ + auto node = rclcpp::Node::make_shared("openvino_image_test"); + rmw_qos_profile_t custom_qos_profile = rmw_qos_profile_default; + custom_qos_profile.depth = 16; + std::promise sub_called; + std::shared_future sub_called_future(sub_called.get_future()); + + auto openvino_faceDetection_callback = + [&sub_called](const object_msgs::msg::ObjectsInBoxes::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_emotionRecognition_callback = + [&sub_called](const people_msgs::msg::EmotionsStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_ageGender_callback = + [&sub_called](const people_msgs::msg::AgeGenderStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + auto openvino_headPose_callback = + [&sub_called](const people_msgs::msg::HeadPoseStamped::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + rclcpp::executors::SingleThreadedExecutor executor; + executor.add_node(node); + + { + auto sub1 = node->create_subscription( + "/openvino_toolkit/detected_objects", openvino_faceDetection_callback, custom_qos_profile); + + auto sub2 = node->create_subscription( + "/ros2_openvino_toolkit/emotions_recognition", openvino_emotionRecognition_callback, + custom_qos_profile); + + auto sub3 = node->create_subscription( + "/ros2_openvino_toolkit/age_genders_Recognition", openvino_ageGender_callback, + custom_qos_profile); + + auto sub4 = node->create_subscription( + "/ros2_openvino_toolkit/headposes_estimation", openvino_headPose_callback, + custom_qos_profile); + + executor.spin_once(std::chrono::seconds(0)); + + wait_for_future(executor, sub_called_future, std::chrono::seconds(10)); + + EXPECT_TRUE(test_pass); + } +} + +int main(int argc, char * argv[]) +{ + testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + auto offset = std::chrono::seconds(30); + system("ros2 launch dynamic_vino_sample pipeline_image_test.launch.py &"); + int ret = RUN_ALL_TESTS(); + rclcpp::sleep_for(offset); + system("killall -s SIGINT pipeline_with_params &"); + rclcpp::shutdown(); + return ret; +} diff --git a/sample/tests/topic/unittest_objectDetectionCheck.cpp b/sample/tests/topic/unittest_objectDetectionCheck.cpp new file mode 100644 index 00000000..5c6377d4 --- /dev/null +++ b/sample/tests/topic/unittest_objectDetectionCheck.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/pipeline.hpp" +#include "dynamic_vino_lib/pipeline_manager.hpp" +#include "dynamic_vino_lib/slog.hpp" +#include "extension/ext_list.hpp" +#include "gflags/gflags.h" +#include "inference_engine.hpp" +#include "librealsense2/rs.hpp" +#include "opencv2/opencv.hpp" +#include "utility.hpp" +static bool test_pass = false; + +template +void wait_for_future( + rclcpp::executor::Executor & executor, std::shared_future & future, + const DurationT & timeout) +{ + using rclcpp::executor::FutureReturnCode; + rclcpp::executor::FutureReturnCode future_ret; + auto start_time = std::chrono::steady_clock::now(); + future_ret = executor.spin_until_future_complete(future, timeout); + auto elapsed_time = std::chrono::steady_clock::now() - start_time; + EXPECT_EQ(FutureReturnCode::SUCCESS, future_ret) << + "the usb camera don't publish data to topic\n" << + "future failed to be set after: " << + std::chrono::duration_cast(elapsed_time).count() << + " milliseconds\n"; +} + +TEST(UnitTestObjectDetection, testObjectDetection) +{ + auto node = rclcpp::Node::make_shared("openvino_objectDetection_test"); + rmw_qos_profile_t custom_qos_profile = rmw_qos_profile_default; + custom_qos_profile.depth = 16; + std::promise sub_called; + std::shared_future sub_called_future(sub_called.get_future()); + + auto openvino_faceDetection_callback = + [&sub_called](const object_msgs::msg::ObjectsInBoxes::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + rclcpp::executors::SingleThreadedExecutor executor; + executor.add_node(node); + + { + auto sub1 = node->create_subscription( + "/ros2_openvino_toolkit/detected_objects", openvino_faceDetection_callback, + custom_qos_profile); + + executor.spin_once(std::chrono::seconds(0)); + + wait_for_future(executor, sub_called_future, std::chrono::seconds(10)); + + EXPECT_TRUE(test_pass); + } +} + +int main(int argc, char * argv[]) +{ + testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + auto offset = std::chrono::seconds(20); + system("ros2 launch dynamic_vino_sample pipeline_object_test.launch.py &"); + int ret = RUN_ALL_TESTS(); + rclcpp::sleep_for(offset); + system("killall -s SIGINT pipeline_with_params &"); + rclcpp::shutdown(); + return ret; +} diff --git a/sample/tests/topic/unittest_segmentationCheck.cpp b/sample/tests/topic/unittest_segmentationCheck.cpp new file mode 100644 index 00000000..3aac18c5 --- /dev/null +++ b/sample/tests/topic/unittest_segmentationCheck.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dynamic_vino_lib/pipeline.hpp" +#include "dynamic_vino_lib/pipeline_manager.hpp" +#include "dynamic_vino_lib/slog.hpp" +#include "extension/ext_list.hpp" +#include "gflags/gflags.h" +#include "inference_engine.hpp" +#include "librealsense2/rs.hpp" +#include "opencv2/opencv.hpp" +#include "utility.hpp" +static bool test_pass = false; + +template +void wait_for_future( + rclcpp::executor::Executor & executor, std::shared_future & future, + const DurationT & timeout) +{ + using rclcpp::executor::FutureReturnCode; + rclcpp::executor::FutureReturnCode future_ret; + auto start_time = std::chrono::steady_clock::now(); + future_ret = executor.spin_until_future_complete(future, timeout); + auto elapsed_time = std::chrono::steady_clock::now() - start_time; + EXPECT_EQ(FutureReturnCode::SUCCESS, future_ret) << + "the usb camera don't publish data to topic\n" << + "future failed to be set after: " << + std::chrono::duration_cast(elapsed_time).count() << + " milliseconds\n"; +} + +TEST(UnitTestObjectDetection, testObjectDetection) +{ + auto node = rclcpp::Node::make_shared("openvino_segmentation_test"); + rmw_qos_profile_t custom_qos_profile = rmw_qos_profile_default; + custom_qos_profile.depth = 16; + std::promise sub_called; + std::shared_future sub_called_future(sub_called.get_future()); + + auto openvino_faceDetection_callback = + [&sub_called](const people_msgs::msg::ObjectsInMasks::SharedPtr msg) -> void { + test_pass = true; + sub_called.set_value(true); + }; + + rclcpp::executors::SingleThreadedExecutor executor; + executor.add_node(node); + + { + auto sub1 = node->create_subscription( + "/ros2_openvino_toolkit/segmented_obejcts", openvino_faceDetection_callback, + custom_qos_profile); + + executor.spin_once(std::chrono::seconds(0)); + + wait_for_future(executor, sub_called_future, std::chrono::seconds(10)); + + EXPECT_TRUE(test_pass); + } +} + +int main(int argc, char * argv[]) +{ + testing::InitGoogleTest(&argc, argv); + rclcpp::init(argc, argv); + auto offset = std::chrono::seconds(10); + system("ros2 launch dynamic_vino_sample pipeline_segmentation_test.launch.py &"); + int ret = RUN_ALL_TESTS(); + rclcpp::sleep_for(offset); + system("killall -s SIGINT pipeline_with_params &"); + rclcpp::shutdown(); + return ret; +} From 7d35153beee76bc14b41b8096e0504223fa02a9c Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:26:48 -0500 Subject: [PATCH 07/16] add unit test parameter and launch --- .../image_object_service_test.launch.py | 33 +++++++++++++++ .../testParam/image_object_service_test.yaml | 19 +++++++++ .../testParam/pipeline_face_test.launch.py | 40 +++++++++++++++++++ .../tests/testParam/pipeline_face_test.yaml | 39 ++++++++++++++++++ .../testParam/pipeline_image_test.launch.py | 40 +++++++++++++++++++ .../tests/testParam/pipeline_image_test.yaml | 40 +++++++++++++++++++ .../testParam/pipeline_object_test.launch.py | 35 ++++++++++++++++ .../tests/testParam/pipeline_object_test.yaml | 19 +++++++++ .../pipeline_segmentation_test.launch.py | 36 +++++++++++++++++ .../testParam/pipeline_segmentation_test.yaml | 19 +++++++++ 10 files changed, 320 insertions(+) create mode 100644 sample/tests/testParam/image_object_service_test.launch.py create mode 100644 sample/tests/testParam/image_object_service_test.yaml create mode 100644 sample/tests/testParam/pipeline_face_test.launch.py create mode 100644 sample/tests/testParam/pipeline_face_test.yaml create mode 100644 sample/tests/testParam/pipeline_image_test.launch.py create mode 100644 sample/tests/testParam/pipeline_image_test.yaml create mode 100644 sample/tests/testParam/pipeline_object_test.launch.py create mode 100644 sample/tests/testParam/pipeline_object_test.yaml create mode 100644 sample/tests/testParam/pipeline_segmentation_test.launch.py create mode 100644 sample/tests/testParam/pipeline_segmentation_test.yaml diff --git a/sample/tests/testParam/image_object_service_test.launch.py b/sample/tests/testParam/image_object_service_test.launch.py new file mode 100644 index 00000000..df75f103 --- /dev/null +++ b/sample/tests/testParam/image_object_service_test.launch.py @@ -0,0 +1,33 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'testParam', + 'image_object_service_test.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='image_object_server', + arguments=['-config', default_yaml], + output='screen'), + ]) diff --git a/sample/tests/testParam/image_object_service_test.yaml b/sample/tests/testParam/image_object_service_test.yaml new file mode 100644 index 00000000..229bf2da --- /dev/null +++ b/sample/tests/testParam/image_object_service_test.yaml @@ -0,0 +1,19 @@ +Pipelines: +- name: object + inputs: [Image] + infers: + - name: ObjectDetection + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/object_detection/common/ssd/300/caffe/output/ssd300.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + outputs: [RosService] + confidence_threshold: 0.2 + connects: + - left: Image + right: [ObjectDetection] + - left: ObjectDetection + right: [RosService] + input_path: "/home/intel/Pictures/car.png" + +Common: diff --git a/sample/tests/testParam/pipeline_face_test.launch.py b/sample/tests/testParam/pipeline_face_test.launch.py new file mode 100644 index 00000000..67f2916e --- /dev/null +++ b/sample/tests/testParam/pipeline_face_test.launch.py @@ -0,0 +1,40 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'testParam', + 'pipeline_face_test.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='pipeline_with_params', + arguments=['-config', default_yaml], + remappings=[ + ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), + ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), + ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], + output='screen'), + ]) diff --git a/sample/tests/testParam/pipeline_face_test.yaml b/sample/tests/testParam/pipeline_face_test.yaml new file mode 100644 index 00000000..7de0bcf1 --- /dev/null +++ b/sample/tests/testParam/pipeline_face_test.yaml @@ -0,0 +1,39 @@ +Pipelines: +- name: people + inputs: [StandardCamera] + infers: + - name: FaceDetection + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml + engine: CPU + label: /opt/intel/computer_vision_sdk/deployment_tools/intel_models/face-detection-adas-0001/FP32/face-detection-adas-0001.labels + batch: 1 + - name: AgeGenderRecognition + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + - name: EmotionRecognition + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003.xml + engine: CPU + label: /opt/intel/computer_vision_sdk/deployment_tools/intel_models/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.labels + batch: 16 + - name: HeadPoseEstimation + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + outputs: [RosTopic] + confidence_threshold: 0.2 + connects: + - left: StandardCamera + right: [FaceDetection] + - left: FaceDetection + right: [AgeGenderRecognition, EmotionRecognition, HeadPoseEstimation, RosTopic] + - left: AgeGenderRecognition + right: [RosTopic] + - left: EmotionRecognition + right: [RosTopic] + - left: HeadPoseEstimation + right: [RosTopic] + +Common: diff --git a/sample/tests/testParam/pipeline_image_test.launch.py b/sample/tests/testParam/pipeline_image_test.launch.py new file mode 100644 index 00000000..f3bbf384 --- /dev/null +++ b/sample/tests/testParam/pipeline_image_test.launch.py @@ -0,0 +1,40 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'testParam', + 'pipeline_image_test.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='pipeline_with_params', + arguments=['-config', default_yaml], + remappings=[ + ('/openvino_toolkit/faces', '/ros2_openvino_toolkit/face_detection'), + ('/openvino_toolkit/emotions', '/ros2_openvino_toolkit/emotions_recognition'), + ('/openvino_toolkit/headposes', '/ros2_openvino_toolkit/headposes_estimation'), + ('/openvino_toolkit/age_genders', + '/ros2_openvino_toolkit/age_genders_Recognition'), + ('/openvino_toolkit/images', '/ros2_openvino_toolkit/image_rviz')], + output='screen'), + ]) diff --git a/sample/tests/testParam/pipeline_image_test.yaml b/sample/tests/testParam/pipeline_image_test.yaml new file mode 100644 index 00000000..be19e01d --- /dev/null +++ b/sample/tests/testParam/pipeline_image_test.yaml @@ -0,0 +1,40 @@ +Pipelines: +- name: people + inputs: [Image] + input_path: /opt/openvino_toolkit/ros2_openvino_toolkit/data/images/team.jpg + infers: + - name: FaceDetection + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Transportation/object_detection/face/pruned_mobilenet_reduced_ssd_shared_weights/dldt/face-detection-adas-0001.xml + engine: CPU + label: /opt/intel/computer_vision_sdk/deployment_tools/intel_models/face-detection-adas-0001/FP32/face-detection-adas-0001.labels + batch: 1 + - name: AgeGenderRecognition + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + - name: EmotionRecognition + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003.xml + engine: CPU + label: /opt/intel/computer_vision_sdk/deployment_tools/intel_models/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.labels + batch: 16 + - name: HeadPoseEstimation + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + outputs: [RosTopic] + confidence_threshold: 0.2 + connects: + - left: Image + right: [FaceDetection] + - left: FaceDetection + right: [AgeGenderRecognition, EmotionRecognition, HeadPoseEstimation, RosTopic] + - left: AgeGenderRecognition + right: [RosTopic] + - left: EmotionRecognition + right: [RosTopic] + - left: HeadPoseEstimation + right: [RosTopic] + +Common: diff --git a/sample/tests/testParam/pipeline_object_test.launch.py b/sample/tests/testParam/pipeline_object_test.launch.py new file mode 100644 index 00000000..9f6556bc --- /dev/null +++ b/sample/tests/testParam/pipeline_object_test.launch.py @@ -0,0 +1,35 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'testParam', + 'pipeline_object_test.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='pipeline_with_params', + arguments=['-config', default_yaml], + remappings=[ + ('/openvino_toolkit/detected_objects', '/ros2_openvino_toolkit/detected_objects')], + output='screen'), + ]) diff --git a/sample/tests/testParam/pipeline_object_test.yaml b/sample/tests/testParam/pipeline_object_test.yaml new file mode 100644 index 00000000..1666674e --- /dev/null +++ b/sample/tests/testParam/pipeline_object_test.yaml @@ -0,0 +1,19 @@ +Pipelines: +- name: object + inputs: [StandardCamera] + infers: + - name: ObjectDetection + model: /opt/openvino_toolkit/open_model_zoo/model_downloader/object_detection/common/ssd/300/caffe/output/ssd300.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 16 + outputs: [RosTopic] + confidence_threshold: 0.2 + connects: + - left: StandardCamera + right: [ObjectDetection] + - left: ObjectDetection + right: [RosTopic] + +OpenvinoCommon: + diff --git a/sample/tests/testParam/pipeline_segmentation_test.launch.py b/sample/tests/testParam/pipeline_segmentation_test.launch.py new file mode 100644 index 00000000..d6de7dfd --- /dev/null +++ b/sample/tests/testParam/pipeline_segmentation_test.launch.py @@ -0,0 +1,36 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'testParam', + 'pipeline_segmentation_test.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='pipeline_with_params', + arguments=['-config', default_yaml], + remappings=[ + ('/openvino_toolkit/segmented_obejcts', + '/ros2_openvino_toolkit/segmented_obejcts')], + output='screen'), + ]) diff --git a/sample/tests/testParam/pipeline_segmentation_test.yaml b/sample/tests/testParam/pipeline_segmentation_test.yaml new file mode 100644 index 00000000..980287e4 --- /dev/null +++ b/sample/tests/testParam/pipeline_segmentation_test.yaml @@ -0,0 +1,19 @@ +Pipelines: +- name: segmentation + inputs: [StandardCamera] + infers: + - name: ObjectSegmentation + model: /opt/models/mask_rcnn_inception_v2_coco_2018_01_28/output/frozen_inference_graph.xml + engine: CPU + label: to/be/set/xxx.labels + batch: 1 + outputs: [RosTopic] + confidence_threshold: 0.2 + connects: + - left: StandardCamera + right: [ObjectSegmentation] + - left: ObjectSegmentation + right: [RosTopic] + +OpenvinoCommon: + From 5ad5a33c1c73ed2c0b4628aceefec378a2aab2eb Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:27:33 -0500 Subject: [PATCH 08/16] add object service launch --- sample/launch/image_object_server.launch.py | 33 +++++++++++++++++++ .../launch/image_object_server_oss.launch.py | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 sample/launch/image_object_server.launch.py create mode 100644 sample/launch/image_object_server_oss.launch.py diff --git a/sample/launch/image_object_server.launch.py b/sample/launch/image_object_server.launch.py new file mode 100644 index 00000000..2d3bbbba --- /dev/null +++ b/sample/launch/image_object_server.launch.py @@ -0,0 +1,33 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'image_object_server.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='image_object_server', + arguments=['-config', default_yaml], + output='screen'), + ]) diff --git a/sample/launch/image_object_server_oss.launch.py b/sample/launch/image_object_server_oss.launch.py new file mode 100644 index 00000000..35d2edef --- /dev/null +++ b/sample/launch/image_object_server_oss.launch.py @@ -0,0 +1,33 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +"""Launch face detection and rviz.""" + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +import launch_ros.actions + + +def generate_launch_description(): + default_yaml = os.path.join(get_package_share_directory('dynamic_vino_sample'), 'param', + 'image_object_server_oss.yaml') + return LaunchDescription([ + # Openvino detection + launch_ros.actions.Node( + package='dynamic_vino_sample', node_executable='image_object_server', + arguments=['-config', default_yaml], + output='screen'), + ]) From c6aa7e6bb4ac29ce2858d3cb85f078829a574405 Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:28:27 -0500 Subject: [PATCH 09/16] add the dependency of unit test --- sample/CMakeLists.txt | 127 +++++++++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 33 deletions(-) diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index cc54f8c7..ebe494a9 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -1,12 +1,14 @@ # Copyright (c) 2018 Intel Corporation - -# 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 - +# 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. + cmake_minimum_required(VERSION 3.5) project(dynamic_vino_sample) @@ -20,7 +22,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() -if (CMAKE_BUILD_TYPE EQUAL "RELEASE") +if(CMAKE_BUILD_TYPE EQUAL "RELEASE") message(STATUS "Create Release Build.") set(CMAKE_CXX_FLAGS "-O2 ${CMAKE_CXX_FLAGS}") else() @@ -74,44 +76,44 @@ if( BUILD_SAMPLE_NAME AND NOT ${BUILD_SAMPLE_NAME} STREQUAL ${PROJECT_NAME} ) return() endif() -set (CpuExtension_lib $ENV{CPU_EXTENSION_LIB}) +set(CpuExtension_lib $ENV{CPU_EXTENSION_LIB}) add_library(cpu_extension SHARED IMPORTED) set_target_properties(cpu_extension PROPERTIES IMPORTED_LOCATION $ENV{CPU_EXTENSION_LIB}) -set (Gflags_lib $ENV{GFLAGS_LIB}) +set(Gflags_lib $ENV{GFLAGS_LIB}) add_library(gflags STATIC IMPORTED) set_target_properties(gflags PROPERTIES IMPORTED_LOCATION $ENV{GFLAGS_LIB}) -file (GLOB MAIN_SRC - ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp - ) +file(GLOB MAIN_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp +) -file (GLOB MAIN_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/*.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h - ) +file(GLOB MAIN_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h +) # Create named folders for the sources within the .vcproj # Empty name lists them directly under the .vcproj source_group("src" FILES ${MAIN_SRC}) source_group("include" FILES ${MAIN_HEADERS}) -include_directories (${OpenCV_INCLUDE_DIRS}) -include_directories (${PROJECT_SOURCE_DIR}/include) -include_directories (${dynamic_vino_lib_INCLUDE_DIRS}) -include_directories (${vino_param_lib_INCLUDE_DIRS}) -include_directories (${InferenceEngine_INCLUDE_DIRS}) -include_directories (${InferenceEngine_INCLUDE_DIRS}/../samples) -include_directories (${InferenceEngine_INCLUDE_DIRS}/../samples/extension) -include_directories (${InferenceEngine_INCLUDE_DIRS}/../src) -include_directories (${InferenceEngine_INCLUDE_DIRS}/../samples/build/thirdparty/gflags/include) -include_directories (${InferenceEngine_INCLUDE_DIRS}/../build/samples/thirdparty/gflags/include) - -include_directories (${librealsense2_INCLUDE_DIRS}) +include_directories(${OpenCV_INCLUDE_DIRS}) +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${dynamic_vino_lib_INCLUDE_DIRS}) +include_directories(${vino_param_lib_INCLUDE_DIRS}) +include_directories(${InferenceEngine_INCLUDE_DIRS}) +include_directories(${InferenceEngine_INCLUDE_DIRS}/../samples) +include_directories(${InferenceEngine_INCLUDE_DIRS}/../samples/extension) +include_directories(${InferenceEngine_INCLUDE_DIRS}/../src) +include_directories(${InferenceEngine_INCLUDE_DIRS}/../samples/build/thirdparty/gflags/include) +include_directories(${InferenceEngine_INCLUDE_DIRS}/../build/samples/thirdparty/gflags/include) + +include_directories(${librealsense2_INCLUDE_DIRS}) #include_directories (/opt/ros2_openvino/include) # Create library file from sources. @@ -128,7 +130,6 @@ ament_target_dependencies(vino_param_sample "vino_param_lib" "dynamic_vino_lib" "rviz_yaml_cpp_vendor" - ) add_executable(pipeline_with_params @@ -208,21 +209,81 @@ install(TARGETS image_object_server install(TARGETS image_object_client DESTINATION lib/${PROJECT_NAME}) -if(BUILD_TESTING) - find_package(ament_lint_auto REQUIRED) - ament_lint_auto_find_test_dependencies() -endif() - # Install param files. -install(DIRECTORY +install(DIRECTORY param DESTINATION share/${PROJECT_NAME}/ ) +# Install param files. +install(DIRECTORY + tests/testParam + DESTINATION share/${PROJECT_NAME}/ +) + # Install launch files. -install(DIRECTORY +install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}/ ) +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + find_package(InferenceEngine REQUIRED) + find_package(rclcpp REQUIRED) + find_package(vino_param_lib REQUIRED) + find_package(OpenCV REQUIRED) + find_package(object_msgs REQUIRED) + find_package(people_msgs REQUIRED) + find_package(ament_index_cpp REQUIRED) + find_package(yaml_cpp_vendor REQUIRED) + find_package(class_loader REQUIRED) + find_package(dynamic_vino_lib REQUIRED) + ament_lint_auto_find_test_dependencies() + + macro(custom_gtest target) + ament_add_gtest(${target} ${ARGN}) + if(TARGET ${target}) + target_include_directories(${target} PUBLIC + ${${PROJECT_NAME}_INCLUDE_DIRS} + ) + target_link_libraries(${target} + dl + cpu_extension + gflags) + ament_target_dependencies(${target} + "rclcpp" + "InferenceEngine" + "vino_param_lib" + "OpenCV" + "object_msgs" + "people_msgs" + "ament_index_cpp" + "yaml_cpp_vendor" + "class_loader" + "dynamic_vino_lib") + endif() + endmacro() + + custom_gtest(unittest_createPipelineCheck + "tests/lib/unittest_createPipelineCheck.cpp" + TIMEOUT 300) + custom_gtest(unittest_faceDetection + "tests/topic/unittest_faceDetectionCheck.cpp" + TIMEOUT 300) + custom_gtest(unittest_objectDetection + "tests/topic/unittest_objectDetectionCheck.cpp" + TIMEOUT 300) + custom_gtest(unittest_imageDetection + "tests/topic/unittest_imageCheck.cpp" + TIMEOUT 300) + custom_gtest(unittest_segmentation + "tests/topic/unittest_segmentationCheck.cpp" + TIMEOUT 300) + custom_gtest(unittest_objectService + "tests/service/unittest_objectService.cpp" + TIMEOUT 300) + +endif() + ament_package() From d2c41da244ecf14257bd1cd02b2caca0c974d4c6 Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:28:44 -0500 Subject: [PATCH 10/16] modify package version --- sample/package.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sample/package.xml b/sample/package.xml index d2e139b5..5a734053 100644 --- a/sample/package.xml +++ b/sample/package.xml @@ -13,13 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> - + dynamic_vino_sample 0.3.0 a ROS2 wrapper package for Intel OpenVINO - Weizhi Liu - Chao Li - Hongkun Chen Weizhi Liu Chao Li Apache License 2.0 @@ -56,6 +53,7 @@ limitations under the License. ament_lint_auto ament_lint_common + ament_cmake_gtest ament_cmake From e8e9915cdf0bd57111e8e62d8ae2630708cf3703 Mon Sep 17 00:00:00 2001 From: RachelRen05 Date: Thu, 31 Jan 2019 04:29:03 -0500 Subject: [PATCH 11/16] add test picture --- data/images/car_vihecle.png | Bin 0 -> 809525 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/images/car_vihecle.png diff --git a/data/images/car_vihecle.png b/data/images/car_vihecle.png new file mode 100644 index 0000000000000000000000000000000000000000..2b2aa223a1cce0578ce2a4ff0697a08b2ff0814a GIT binary patch literal 809525 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

~tAfaBs{SuGZ?J(ncaRnek1EbJOQs*wc? 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