summaryrefslogtreecommitdiff
path: root/python/openvino/runtime/common/demo_utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'python/openvino/runtime/common/demo_utils/src')
-rw-r--r--python/openvino/runtime/common/demo_utils/src/args_helper.cpp155
-rw-r--r--python/openvino/runtime/common/demo_utils/src/config_factory.cpp111
-rw-r--r--python/openvino/runtime/common/demo_utils/src/image_utils.cpp55
-rw-r--r--python/openvino/runtime/common/demo_utils/src/images_capture.cpp327
-rw-r--r--python/openvino/runtime/common/demo_utils/src/kuhn_munkres.cpp169
-rw-r--r--python/openvino/runtime/common/demo_utils/src/performance_metrics.cpp114
-rw-r--r--python/openvino/runtime/common/demo_utils/src/w_dirent.hpp114
7 files changed, 1045 insertions, 0 deletions
diff --git a/python/openvino/runtime/common/demo_utils/src/args_helper.cpp b/python/openvino/runtime/common/demo_utils/src/args_helper.cpp
new file mode 100644
index 0000000..8f4bc35
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/args_helper.cpp
@@ -0,0 +1,155 @@
+// Copyright (C) 2018-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "utils/args_helper.hpp"
+#include "utils/slog.hpp"
+
+#ifdef _WIN32
+#include "w_dirent.hpp"
+#else
+#include <dirent.h>
+#endif
+
+#include <gflags/gflags.h>
+
+#include <sys/stat.h>
+#include <map>
+
+#include <algorithm>
+#include <cctype>
+#include <sstream>
+
+void readInputFilesArguments(std::vector<std::string>& files, const std::string& arg) {
+ struct stat sb;
+ if (stat(arg.c_str(), &sb) != 0) {
+ if (arg.compare(0, 5, "rtsp:") != 0) {
+ slog::warn << "File " << arg << " cannot be opened!" << slog::endl;
+ return;
+ }
+ }
+ if (S_ISDIR(sb.st_mode)) {
+ DIR *dp;
+ dp = opendir(arg.c_str());
+ if (dp == nullptr) {
+ slog::warn << "Directory " << arg << " cannot be opened!" << slog::endl;
+ return;
+ }
+
+ struct dirent *ep;
+ while (nullptr != (ep = readdir(dp))) {
+ std::string fileName = ep->d_name;
+ if (fileName == "." || fileName == "..") continue;
+ files.push_back(arg + "/" + ep->d_name);
+ }
+ closedir(dp);
+ } else {
+ files.push_back(arg);
+ }
+}
+
+void parseInputFilesArguments(std::vector<std::string>& files) {
+ std::vector<std::string> args = gflags::GetArgvs();
+ bool readArguments = false;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args.at(i) == "-i" || args.at(i) == "--i") {
+ readArguments = true;
+ continue;
+ }
+ if (!readArguments) {
+ continue;
+ }
+ if (args.at(i).c_str()[0] == '-') {
+ break;
+ }
+ readInputFilesArguments(files, args.at(i));
+ }
+}
+
+std::vector<std::string> split(const std::string& s, char delim) {
+ std::vector<std::string> result;
+ std::stringstream ss(s);
+ std::string item;
+
+ while (getline(ss, item, delim)) {
+ result.push_back(item);
+ }
+ return result;
+}
+
+std::vector<std::string> parseDevices(const std::string& device_string) {
+ const std::string::size_type colon_position = device_string.find(":");
+ if (colon_position != std::string::npos) {
+ std::string device_type = device_string.substr(0, colon_position);
+ if (device_type == "HETERO" || device_type == "MULTI") {
+ std::string comma_separated_devices = device_string.substr(colon_position + 1);
+ std::vector<std::string> devices = split(comma_separated_devices, ',');
+ for (auto& device : devices)
+ device = device.substr(0, device.find("("));
+ return devices;
+ }
+ }
+ return {device_string};
+}
+
+// Format: <device1>:<value1>,<device2>:<value2> or just <value>
+std::map<std::string, int32_t> parseValuePerDevice(const std::set<std::string>& devices,
+ const std::string& values_string) {
+ auto values_string_upper = values_string;
+ std::transform(values_string_upper.begin(),
+ values_string_upper.end(),
+ values_string_upper.begin(),
+ [](unsigned char c){ return std::toupper(c); });
+ std::map<std::string, int32_t> result;
+ auto device_value_strings = split(values_string_upper, ',');
+ for (auto& device_value_string : device_value_strings) {
+ auto device_value_vec = split(device_value_string, ':');
+ if (device_value_vec.size() == 2) {
+ auto it = std::find(devices.begin(), devices.end(), device_value_vec.at(0));
+ if (it != devices.end()) {
+ result[device_value_vec.at(0)] = std::stoi(device_value_vec.at(1));
+ }
+ } else if (device_value_vec.size() == 1) {
+ uint32_t value = std::stoi(device_value_vec.at(0));
+ for (const auto& device : devices) {
+ result[device] = value;
+ }
+ } else if (device_value_vec.size() != 0) {
+ throw std::runtime_error("Unknown string format: " + values_string);
+ }
+ }
+ return result;
+}
+
+cv::Size stringToSize(const std::string& str) {
+ std::vector<std::string> strings = split(str, 'x');
+ if (strings.size() != 2) {
+ throw std::invalid_argument("Can't convert std::string to cv::Size. The string must contain exactly one x");
+ }
+ return {std::stoi(strings[0]), std::stoi(strings[1])};
+}
+
+std::map<std::string, ov::Layout> parseLayoutString(const std::string& layout_string) {
+ // Parse parameter string like "input0:NCHW,input1:NC" or "NCHW" (applied to all
+ // inputs)
+ std::map<std::string, ov::Layout> layouts;
+ std::string searchStr = (layout_string.find_last_of(':') == std::string::npos && !layout_string.empty() ?
+ ":" : "") + layout_string;
+ auto colonPos = searchStr.find_last_of(':');
+ while (colonPos != std::string::npos) {
+ auto startPos = searchStr.find_last_of(',');
+ auto inputName = searchStr.substr(startPos + 1, colonPos - startPos - 1);
+ auto inputLayout = searchStr.substr(colonPos + 1);
+ layouts[inputName] = ov::Layout(inputLayout);
+ searchStr = searchStr.substr(0, startPos + 1);
+ if (searchStr.empty() || searchStr.back() != ',') {
+ break;
+ }
+ searchStr.pop_back();
+ colonPos = searchStr.find_last_of(':');
+ }
+ if (!searchStr.empty()) {
+ throw std::invalid_argument("Can't parse input layout string: " + layout_string);
+ }
+ return layouts;
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/config_factory.cpp b/python/openvino/runtime/common/demo_utils/src/config_factory.cpp
new file mode 100644
index 0000000..2e9a442
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/config_factory.cpp
@@ -0,0 +1,111 @@
+/*
+// Copyright (C) 2020-2022 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 "utils/config_factory.h"
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <openvino/runtime/intel_gpu/properties.hpp>
+#include "dla_plugin_config.hpp"
+#include "utils/args_helper.hpp"
+#include <sys/stat.h>
+
+std::set<std::string> ModelConfig::getDevices() {
+ if (devices.empty()) {
+ for (const std::string& device : parseDevices(deviceName)) {
+ devices.insert(device);
+ }
+ }
+
+ return devices;
+}
+
+ModelConfig ConfigFactory::getUserConfig(const std::string& flags_d,
+ uint32_t flags_nireq,
+ const std::string& flags_nstreams,
+ uint32_t flags_nthreads,
+ const std::string &flags_arch) {
+ auto config = getCommonConfig(flags_d, flags_nireq);
+
+ std::map<std::string, int> deviceNstreams = parseValuePerDevice(config.getDevices(), flags_nstreams);
+ for (const auto& device : config.getDevices()) {
+ if (flags_arch != "" && device == "FPGA") {
+ struct stat buffer;
+ if (stat(flags_arch.c_str(), &buffer) != 0) {
+ std::cout << "Error: architecture file: " << flags_arch << " doesn't exist. Please provide a valid path." << std::endl;
+ throw std::logic_error("architecture file path does not exist.");
+ }
+ config.compiledModelConfig.emplace(DLIAPlugin::properties::arch_path.name(), flags_arch);
+ } else if (device == "CPU") { // CPU supports a few special performance-oriented keys
+ // limit threading for CPU portion of inference
+ if (flags_nthreads != 0)
+ config.compiledModelConfig.emplace(ov::inference_num_threads.name(), flags_nthreads);
+
+ config.compiledModelConfig.emplace(ov::affinity.name(), ov::Affinity::NONE);
+
+ ov::streams::Num nstreams =
+ deviceNstreams.count(device) > 0 ? ov::streams::Num(deviceNstreams[device]) : ov::streams::AUTO;
+ config.compiledModelConfig.emplace(ov::streams::num.name(), nstreams);
+ } else if (device == "GPU") {
+ ov::streams::Num nstreams =
+ deviceNstreams.count(device) > 0 ? ov::streams::Num(deviceNstreams[device]) : ov::streams::AUTO;
+ config.compiledModelConfig.emplace(ov::streams::num.name(), nstreams);
+ if (flags_d.find("MULTI") != std::string::npos &&
+ config.getDevices().find("CPU") != config.getDevices().end()) {
+ // multi-device execution with the CPU + GPU performs best with GPU throttling hint,
+ // which releases another CPU thread (that is otherwise used by the GPU driver for active polling)
+ config.compiledModelConfig.emplace(ov::intel_gpu::hint::queue_throttle.name(),
+ ov::intel_gpu::hint::ThrottleLevel(1));
+ }
+ }
+ }
+ return config;
+}
+
+ModelConfig ConfigFactory::getMinLatencyConfig(const std::string& flags_d, uint32_t flags_nireq) {
+ auto config = getCommonConfig(flags_d, flags_nireq);
+ for (const auto& device : config.getDevices()) {
+ if (device == "CPU") { // CPU supports a few special performance-oriented keys
+ config.compiledModelConfig.emplace(ov::streams::num.name(), 1);
+ } else if (device == "GPU") {
+ config.compiledModelConfig.emplace(ov::streams::num.name(), 1);
+ }
+ }
+ return config;
+}
+
+ModelConfig ConfigFactory::getCommonConfig(const std::string& flags_d, uint32_t flags_nireq) {
+ ModelConfig config;
+
+ if (!flags_d.empty()) {
+ config.deviceName = flags_d;
+ }
+
+ config.maxAsyncRequests = flags_nireq;
+
+ return config;
+}
+
+std::map<std::string, std::string> ModelConfig::getLegacyConfig() {
+ std::map<std::string, std::string> config;
+ for (const auto& item : compiledModelConfig) {
+ config[item.first] = item.second.as<std::string>();
+ }
+ return config;
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/image_utils.cpp b/python/openvino/runtime/common/demo_utils/src/image_utils.cpp
new file mode 100644
index 0000000..039dd66
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/image_utils.cpp
@@ -0,0 +1,55 @@
+/*
+// Copyright (C) 2021-2022 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 "utils/image_utils.h"
+
+cv::Mat resizeImageExt(const cv::Mat& mat, int width, int height, RESIZE_MODE resizeMode,
+ cv::InterpolationFlags interpolationMode, cv::Rect* roi, cv::Scalar BorderConstant) {
+ if (width == mat.cols && height == mat.rows) {
+ return mat;
+ }
+
+ cv::Mat dst;
+
+ switch (resizeMode) {
+ case RESIZE_FILL:
+ {
+ cv::resize(mat, dst, cv::Size(width, height), interpolationMode);
+ if (roi) {
+ *roi = cv::Rect(0, 0, width, height);
+ }
+ break;
+ }
+ case RESIZE_KEEP_ASPECT:
+ case RESIZE_KEEP_ASPECT_LETTERBOX:
+ {
+ double scale = std::min(static_cast<double>(width) / mat.cols, static_cast<double>(height) / mat.rows);
+ cv::Mat resizedImage;
+ cv::resize(mat, resizedImage, cv::Size(0, 0), scale, scale, interpolationMode);
+
+ int dx = resizeMode == RESIZE_KEEP_ASPECT ? 0 : (width - resizedImage.cols) / 2;
+ int dy = resizeMode == RESIZE_KEEP_ASPECT ? 0 : (height - resizedImage.rows) / 2;
+
+ cv::copyMakeBorder(resizedImage, dst, dy, height - resizedImage.rows - dy,
+ dx, width - resizedImage.cols - dx, cv::BORDER_CONSTANT, BorderConstant);
+ if (roi) {
+ *roi = cv::Rect(dx, dy, resizedImage.cols, resizedImage.rows);
+ }
+ break;
+ }
+ }
+ return dst;
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/images_capture.cpp b/python/openvino/runtime/common/demo_utils/src/images_capture.cpp
new file mode 100644
index 0000000..febcdd7
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/images_capture.cpp
@@ -0,0 +1,327 @@
+// Copyright (C) 2020-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+#include "utils/images_capture.h"
+
+#include <string.h>
+
+#ifdef _WIN32
+# include "w_dirent.hpp"
+#else
+# include <dirent.h> // for closedir, dirent, opendir, readdir, DIR
+#endif
+
+#include <algorithm>
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <opencv2/imgcodecs.hpp>
+#include <opencv2/videoio.hpp>
+
+class InvalidInput : public std::runtime_error {
+public:
+ explicit InvalidInput(const std::string& message) noexcept : std::runtime_error(message) {}
+};
+
+class OpenError : public std::runtime_error {
+public:
+ explicit OpenError(const std::string& message) noexcept : std::runtime_error(message) {}
+};
+
+class ImreadWrapper : public ImagesCapture {
+ cv::Mat img;
+ bool canRead;
+
+public:
+ ImreadWrapper(const std::string& input, bool loop) : ImagesCapture{loop}, canRead{true} {
+ auto startTime = std::chrono::steady_clock::now();
+
+ std::ifstream file(input.c_str());
+ if (!file.good())
+ throw InvalidInput("Can't find the image by " + input);
+
+ img = cv::imread(input);
+ if (!img.data)
+ throw OpenError("Can't open the image from " + input);
+ else
+ readerMetrics.update(startTime);
+ }
+
+ double fps() const override {
+ return 1.0;
+ }
+
+ std::string getType() const override {
+ return "IMAGE";
+ }
+
+ cv::Mat read() override {
+ if (loop)
+ return img.clone();
+ if (canRead) {
+ canRead = false;
+ return img.clone();
+ }
+ return cv::Mat{};
+ }
+};
+
+class DirReader : public ImagesCapture {
+ std::vector<std::string> names;
+ size_t fileId;
+ size_t nextImgId;
+ const size_t initialImageId;
+ const size_t readLengthLimit;
+ const std::string input;
+
+public:
+ DirReader(const std::string& input, bool loop, size_t initialImageId, size_t readLengthLimit)
+ : ImagesCapture{loop},
+ fileId{0},
+ nextImgId{0},
+ initialImageId{initialImageId},
+ readLengthLimit{readLengthLimit},
+ input{input} {
+ DIR* dir = opendir(input.c_str());
+ if (!dir)
+ throw InvalidInput("Can't find the dir by " + input);
+ while (struct dirent* ent = readdir(dir))
+ if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
+ names.emplace_back(ent->d_name);
+ closedir(dir);
+ if (names.empty())
+ throw OpenError("The dir " + input + " is empty");
+ sort(names.begin(), names.end());
+ size_t readImgs = 0;
+ while (fileId < names.size()) {
+ cv::Mat img = cv::imread(input + '/' + names[fileId]);
+ if (img.data) {
+ ++readImgs;
+ if (readImgs - 1 >= initialImageId)
+ return;
+ }
+ ++fileId;
+ }
+ throw OpenError("Can't read the first image from " + input);
+ }
+
+ double fps() const override {
+ return 1.0;
+ }
+
+ std::string getType() const override {
+ return "DIR";
+ }
+
+ cv::Mat read() override {
+ auto startTime = std::chrono::steady_clock::now();
+
+ while (fileId < names.size() && nextImgId < readLengthLimit) {
+ cv::Mat img = cv::imread(input + '/' + names[fileId]);
+ ++fileId;
+ if (img.data) {
+ ++nextImgId;
+ readerMetrics.update(startTime);
+ return img;
+ }
+ }
+
+ if (loop) {
+ fileId = 0;
+ size_t readImgs = 0;
+ while (fileId < names.size()) {
+ cv::Mat img = cv::imread(input + '/' + names[fileId]);
+ ++fileId;
+ if (img.data) {
+ ++readImgs;
+ if (readImgs - 1 >= initialImageId) {
+ nextImgId = 1;
+ readerMetrics.update(startTime);
+ return img;
+ }
+ }
+ }
+ }
+ return cv::Mat{};
+ }
+};
+
+class VideoCapWrapper : public ImagesCapture {
+ cv::VideoCapture cap;
+ bool first_read;
+ const read_type type;
+ size_t nextImgId;
+ const double initialImageId;
+ size_t readLengthLimit;
+
+public:
+ VideoCapWrapper(const std::string& input, bool loop, read_type type, size_t initialImageId, size_t readLengthLimit)
+ : ImagesCapture{loop},
+ first_read{true},
+ type{type},
+ nextImgId{0},
+ initialImageId{static_cast<double>(initialImageId)} {
+ if (0 == readLengthLimit) {
+ throw std::runtime_error("readLengthLimit must be positive");
+ }
+ if (cap.open(input)) {
+ this->readLengthLimit = readLengthLimit;
+ if (!cap.set(cv::CAP_PROP_POS_FRAMES, this->initialImageId))
+ throw OpenError("Can't set the frame to begin with");
+ return;
+ }
+ throw InvalidInput("Can't open the video from " + input);
+ }
+
+ double fps() const override {
+ return cap.get(cv::CAP_PROP_FPS);
+ }
+
+ std::string getType() const override {
+ return "VIDEO";
+ }
+
+ cv::Mat read() override {
+ auto startTime = std::chrono::steady_clock::now();
+
+ if (nextImgId >= readLengthLimit) {
+ if (loop && cap.set(cv::CAP_PROP_POS_FRAMES, initialImageId)) {
+ nextImgId = 1;
+ cv::Mat img;
+ cap.read(img);
+ if (type == read_type::safe) {
+ img = img.clone();
+ }
+ readerMetrics.update(startTime);
+ return img;
+ }
+ return cv::Mat{};
+ }
+ cv::Mat img;
+ bool success = cap.read(img);
+ if (!success && first_read) {
+ throw std::runtime_error("The first image can't be read");
+ }
+ first_read = false;
+ if (!success && loop && cap.set(cv::CAP_PROP_POS_FRAMES, initialImageId)) {
+ nextImgId = 1;
+ cap.read(img);
+ } else {
+ ++nextImgId;
+ }
+ if (type == read_type::safe) {
+ img = img.clone();
+ }
+ readerMetrics.update(startTime);
+ return img;
+ }
+};
+
+class CameraCapWrapper : public ImagesCapture {
+ cv::VideoCapture cap;
+ const read_type type;
+ size_t nextImgId;
+ size_t readLengthLimit;
+
+public:
+ CameraCapWrapper(const std::string& input,
+ bool loop,
+ read_type type,
+ size_t readLengthLimit,
+ cv::Size cameraResolution)
+ : ImagesCapture{loop},
+ type{type},
+ nextImgId{0} {
+ if (0 == readLengthLimit) {
+ throw std::runtime_error("readLengthLimit must be positive");
+ }
+ try {
+ if (cap.open(std::stoi(input))) {
+ this->readLengthLimit = loop ? std::numeric_limits<size_t>::max() : readLengthLimit;
+ cap.set(cv::CAP_PROP_BUFFERSIZE, 1);
+ cap.set(cv::CAP_PROP_FRAME_WIDTH, cameraResolution.width);
+ cap.set(cv::CAP_PROP_FRAME_HEIGHT, cameraResolution.height);
+ cap.set(cv::CAP_PROP_AUTOFOCUS, true);
+ cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));
+ return;
+ }
+ throw OpenError("Can't open the camera from " + input);
+ } catch (const std::invalid_argument&) {
+ throw InvalidInput("Can't find the camera " + input);
+ } catch (const std::out_of_range&) { throw InvalidInput("Can't find the camera " + input); }
+ }
+
+ double fps() const override {
+ return cap.get(cv::CAP_PROP_FPS) > 0 ? cap.get(cv::CAP_PROP_FPS) : 30;
+ }
+
+ std::string getType() const override {
+ return "CAMERA";
+ }
+
+ cv::Mat read() override {
+ auto startTime = std::chrono::steady_clock::now();
+
+ if (nextImgId >= readLengthLimit) {
+ return cv::Mat{};
+ }
+ cv::Mat img;
+ if (!cap.read(img)) {
+ throw std::runtime_error("The image can't be captured from the camera");
+ }
+ if (type == read_type::safe) {
+ img = img.clone();
+ }
+ ++nextImgId;
+
+ readerMetrics.update(startTime);
+ return img;
+ }
+};
+
+std::unique_ptr<ImagesCapture> openImagesCapture(const std::string& input,
+ bool loop,
+ read_type type,
+ size_t initialImageId,
+ size_t readLengthLimit,
+ cv::Size cameraResolution
+ ) {
+ if (readLengthLimit == 0)
+ throw std::runtime_error{"Read length limit must be positive"};
+ std::vector<std::string> invalidInputs, openErrors;
+ try {
+ return std::unique_ptr<ImagesCapture>(new ImreadWrapper{input, loop});
+ } catch (const InvalidInput& e) { invalidInputs.push_back(e.what()); } catch (const OpenError& e) {
+ openErrors.push_back(e.what());
+ }
+
+ try {
+ return std::unique_ptr<ImagesCapture>(new DirReader{input, loop, initialImageId, readLengthLimit});
+ } catch (const InvalidInput& e) { invalidInputs.push_back(e.what()); } catch (const OpenError& e) {
+ openErrors.push_back(e.what());
+ }
+
+ try {
+ return std::unique_ptr<ImagesCapture>(new VideoCapWrapper{input, loop, type, initialImageId, readLengthLimit});
+ } catch (const InvalidInput& e) { invalidInputs.push_back(e.what()); } catch (const OpenError& e) {
+ openErrors.push_back(e.what());
+ }
+
+ try {
+ return std::unique_ptr<ImagesCapture>(
+ new CameraCapWrapper{input, loop, type, readLengthLimit, cameraResolution});
+ } catch (const InvalidInput& e) { invalidInputs.push_back(e.what()); } catch (const OpenError& e) {
+ openErrors.push_back(e.what());
+ }
+
+ std::vector<std::string> errorMessages = openErrors.empty() ? invalidInputs : openErrors;
+ std::string errorsInfo;
+ for (const auto& message : errorMessages) {
+ errorsInfo.append(message + "\n");
+ }
+ throw std::runtime_error(errorsInfo);
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/kuhn_munkres.cpp b/python/openvino/runtime/common/demo_utils/src/kuhn_munkres.cpp
new file mode 100644
index 0000000..7d612c1
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/kuhn_munkres.cpp
@@ -0,0 +1,169 @@
+// Copyright (C) 2018-2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include <utils/kuhn_munkres.hpp>
+
+KuhnMunkres::KuhnMunkres(bool greedy) : n_(), greedy_(greedy) {}
+
+std::vector<size_t> KuhnMunkres::Solve(const cv::Mat& dissimilarity_matrix) {
+ CV_Assert(dissimilarity_matrix.type() == CV_32F);
+ double min_val;
+ cv::minMaxLoc(dissimilarity_matrix, &min_val);
+
+ n_ = std::max(dissimilarity_matrix.rows, dissimilarity_matrix.cols);
+ dm_ = cv::Mat(n_, n_, CV_32F, cv::Scalar(0));
+ marked_ = cv::Mat(n_, n_, CV_8S, cv::Scalar(0));
+ points_ = std::vector<cv::Point>(n_ * 2);
+
+ dissimilarity_matrix.copyTo(dm_(
+ cv::Rect(0, 0, dissimilarity_matrix.cols, dissimilarity_matrix.rows)));
+
+ is_row_visited_ = std::vector<int>(n_, 0);
+ is_col_visited_ = std::vector<int>(n_, 0);
+
+ Run();
+
+ std::vector<size_t> results(dissimilarity_matrix.rows, -1);
+ for (int i = 0; i < dissimilarity_matrix.rows; i++) {
+ const auto ptr = marked_.ptr<char>(i);
+ for (int j = 0; j < dissimilarity_matrix.cols; j++) {
+ if (ptr[j] == kStar) {
+ results[i] = (size_t)j;
+ }
+ }
+ }
+ return results;
+}
+
+void KuhnMunkres::TrySimpleCase() {
+ auto is_row_visited = std::vector<int>(n_, 0);
+ auto is_col_visited = std::vector<int>(n_, 0);
+
+ for (int row = 0; row < n_; row++) {
+ auto ptr = dm_.ptr<float>(row);
+ auto marked_ptr = marked_.ptr<char>(row);
+ auto min_val = *std::min_element(ptr, ptr + n_);
+ for (int col = 0; col < n_; col++) {
+ ptr[col] -= min_val;
+ if (ptr[col] == 0 && !is_col_visited[col] && !is_row_visited[row]) {
+ marked_ptr[col] = kStar;
+ is_col_visited[col] = 1;
+ is_row_visited[row] = 1;
+ }
+ }
+ }
+}
+
+bool KuhnMunkres::CheckIfOptimumIsFound() {
+ int count = 0;
+ for (int i = 0; i < n_; i++) {
+ const auto marked_ptr = marked_.ptr<char>(i);
+ for (int j = 0; j < n_; j++) {
+ if (marked_ptr[j] == kStar) {
+ is_col_visited_[j] = 1;
+ count++;
+ }
+ }
+ }
+
+ return count >= n_;
+}
+
+cv::Point KuhnMunkres::FindUncoveredMinValPos() {
+ auto min_val = std::numeric_limits<float>::max();
+ cv::Point min_val_pos(-1, -1);
+ for (int i = 0; i < n_; i++) {
+ if (!is_row_visited_[i]) {
+ auto dm_ptr = dm_.ptr<float>(i);
+ for (int j = 0; j < n_; j++) {
+ if (!is_col_visited_[j] && dm_ptr[j] < min_val) {
+ min_val = dm_ptr[j];
+ min_val_pos = cv::Point(j, i);
+ }
+ }
+ }
+ }
+ return min_val_pos;
+}
+
+void KuhnMunkres::UpdateDissimilarityMatrix(float val) {
+ for (int i = 0; i < n_; i++) {
+ auto dm_ptr = dm_.ptr<float>(i);
+ for (int j = 0; j < n_; j++) {
+ if (is_row_visited_[i]) dm_ptr[j] += val;
+ if (!is_col_visited_[j]) dm_ptr[j] -= val;
+ }
+ }
+}
+
+int KuhnMunkres::FindInRow(int row, int what) {
+ for (int j = 0; j < n_; j++) {
+ if (marked_.at<char>(row, j) == what) {
+ return j;
+ }
+ }
+ return -1;
+}
+
+int KuhnMunkres::FindInCol(int col, int what) {
+ for (int i = 0; i < n_; i++) {
+ if (marked_.at<char>(i, col) == what) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void KuhnMunkres::Run() {
+ TrySimpleCase();
+ if (greedy_)
+ return;
+ while (!CheckIfOptimumIsFound()) {
+ while (true) {
+ auto point = FindUncoveredMinValPos();
+ auto min_val = dm_.at<float>(point.y, point.x);
+ if (min_val > 0) {
+ UpdateDissimilarityMatrix(min_val);
+ } else {
+ marked_.at<char>(point.y, point.x) = kPrime;
+ int col = FindInRow(point.y, kStar);
+ if (col >= 0) {
+ is_row_visited_[point.y] = 1;
+ is_col_visited_[col] = 0;
+ } else {
+ int count = 0;
+ points_[count] = point;
+
+ while (true) {
+ int row = FindInCol(points_[count].x, kStar);
+ if (row >= 0) {
+ count++;
+ points_[count] = cv::Point(points_[count - 1].x, row);
+ int col = FindInRow(points_[count].y, kPrime);
+ count++;
+ points_[count] = cv::Point(col, points_[count - 1].y);
+ } else {
+ break;
+ }
+ }
+
+ for (int i = 0; i < count + 1; i++) {
+ auto& mark = marked_.at<char>(points_[i].y, points_[i].x);
+ mark = mark == kStar ? 0 : kStar;
+ }
+
+ is_row_visited_ = std::vector<int>(n_, 0);
+ is_col_visited_ = std::vector<int>(n_, 0);
+
+ marked_.setTo(0, marked_ == kPrime);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/performance_metrics.cpp b/python/openvino/runtime/common/demo_utils/src/performance_metrics.cpp
new file mode 100644
index 0000000..d1e494e
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/performance_metrics.cpp
@@ -0,0 +1,114 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <limits>
+#include "utils/performance_metrics.hpp"
+#include "utils/slog.hpp"
+
+// timeWindow defines the length of the timespan over which the 'current fps' value is calculated
+PerformanceMetrics::PerformanceMetrics(Duration timeWindow)
+ : timeWindowSize(timeWindow)
+ , firstFrameProcessed(false)
+{}
+
+void PerformanceMetrics::update(TimePoint lastRequestStartTime,
+ const cv::Mat& frame,
+ cv::Point position,
+ int fontFace,
+ double fontScale,
+ cv::Scalar color,
+ int thickness,
+ MetricTypes metricType) {
+ update(lastRequestStartTime);
+ paintMetrics(frame, position, fontFace, fontScale, color, thickness, metricType);
+}
+
+void PerformanceMetrics::update(TimePoint lastRequestStartTime) {
+ TimePoint currentTime = Clock::now();
+
+ if (!firstFrameProcessed) {
+ lastUpdateTime = lastRequestStartTime;
+ firstFrameProcessed = true;
+ }
+
+ currentMovingStatistic.latency += currentTime - lastRequestStartTime;
+ currentMovingStatistic.period = currentTime - lastUpdateTime;
+ currentMovingStatistic.frameCount++;
+
+ if (currentTime - lastUpdateTime > timeWindowSize) {
+ lastMovingStatistic = currentMovingStatistic;
+ totalStatistic.combine(lastMovingStatistic);
+ currentMovingStatistic = Statistic();
+
+ lastUpdateTime = currentTime;
+ }
+}
+
+void PerformanceMetrics::paintMetrics(const cv::Mat& frame, cv::Point position, int fontFace,
+ double fontScale, cv::Scalar color, int thickness, MetricTypes metricType) const {
+ // Draw performance stats over frame
+ Metrics metrics = getLast();
+
+ std::ostringstream out;
+ if (!std::isnan(metrics.latency) &&
+ (metricType == PerformanceMetrics::MetricTypes::LATENCY || metricType == PerformanceMetrics::MetricTypes::ALL)) {
+ out << "Latency: " << std::fixed << std::setprecision(1) << metrics.latency << " ms";
+ putHighlightedText(frame, out.str(), position, fontFace, fontScale, color, thickness);
+ }
+ if (!std::isnan(metrics.fps) &&
+ (metricType == PerformanceMetrics::MetricTypes::FPS || metricType == PerformanceMetrics::MetricTypes::ALL)) {
+ out.str("");
+ out << "FPS: " << std::fixed << std::setprecision(1) << metrics.fps;
+ int offset = metricType == PerformanceMetrics::MetricTypes::ALL ? 30 : 0;
+ putHighlightedText(frame, out.str(), {position.x, position.y + offset}, fontFace, fontScale, color, thickness);
+ }
+}
+
+PerformanceMetrics::Metrics PerformanceMetrics::getLast() const {
+ Metrics metrics;
+
+ metrics.latency = lastMovingStatistic.frameCount != 0
+ ? std::chrono::duration_cast<Ms>(lastMovingStatistic.latency).count()
+ / lastMovingStatistic.frameCount
+ : std::numeric_limits<double>::signaling_NaN();
+ metrics.fps = lastMovingStatistic.period != Duration::zero()
+ ? lastMovingStatistic.frameCount
+ / std::chrono::duration_cast<Sec>(lastMovingStatistic.period).count()
+ : std::numeric_limits<double>::signaling_NaN();
+
+ return metrics;
+}
+
+PerformanceMetrics::Metrics PerformanceMetrics::getTotal() const {
+ Metrics metrics;
+
+ int frameCount = totalStatistic.frameCount + currentMovingStatistic.frameCount;
+ if (frameCount != 0) {
+ metrics.latency = std::chrono::duration_cast<Ms>(
+ totalStatistic.latency + currentMovingStatistic.latency).count() / frameCount;
+ metrics.fps = frameCount / std::chrono::duration_cast<Sec>(
+ totalStatistic.period + currentMovingStatistic.period).count();
+ } else {
+ metrics.latency = std::numeric_limits<double>::signaling_NaN();
+ metrics.fps = std::numeric_limits<double>::signaling_NaN();
+ }
+
+ return metrics;
+}
+
+void PerformanceMetrics::logTotal() const {
+ Metrics metrics = getTotal();
+
+ slog::info << "\tLatency: " << std::fixed << std::setprecision(1) << metrics.latency << " ms" << slog::endl;
+ slog::info << "\tFPS: " << metrics.fps << slog::endl;
+}
+
+void logLatencyPerStage(double readLat, double preprocLat, double inferLat, double postprocLat, double renderLat) {
+ slog::info << "\tDecoding:\t" << std::fixed << std::setprecision(1) <<
+ readLat << " ms" << slog::endl;
+ slog::info << "\tPreprocessing:\t" << preprocLat << " ms" << slog::endl;
+ slog::info << "\tInference:\t" << inferLat << " ms" << slog::endl;
+ slog::info << "\tPostprocessing:\t" << postprocLat << " ms" << slog::endl;
+ slog::info << "\tRendering:\t" << renderLat << " ms" << slog::endl;
+}
diff --git a/python/openvino/runtime/common/demo_utils/src/w_dirent.hpp b/python/openvino/runtime/common/demo_utils/src/w_dirent.hpp
new file mode 100644
index 0000000..0df8636
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/src/w_dirent.hpp
@@ -0,0 +1,114 @@
+// Copyright (C) 2018-2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#if defined(_WIN32)
+
+#ifndef NOMINMAX
+# define NOMINMAX
+#endif
+
+#include <WinSock2.h>
+#include <Windows.h>
+#include <stdlib.h>
+
+#else
+
+#include <unistd.h>
+#include <cstdlib>
+#include <string.h>
+
+#endif
+
+#include <string>
+
+#include <sys/stat.h>
+
+#if defined(WIN32)
+ // Copied from linux libc sys/stat.h:
+ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+ #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+struct dirent {
+ char *d_name;
+
+ explicit dirent(const wchar_t *wsFilePath) {
+ size_t i;
+ auto slen = wcslen(wsFilePath);
+ d_name = static_cast<char*>(malloc(slen + 1));
+ wcstombs_s(&i, d_name, slen + 1, wsFilePath, slen);
+ }
+
+ ~dirent() {
+ free(d_name);
+ }
+};
+
+class DIR {
+ WIN32_FIND_DATAA FindFileData;
+ HANDLE hFind;
+ dirent *next;
+
+ static inline bool endsWith(const std::string &src, const char *with) {
+ int wl = static_cast<int>(strlen(with));
+ int so = static_cast<int>(src.length()) - wl;
+ if (so < 0) return false;
+ return 0 == strncmp(with, &src[so], wl);
+ }
+
+public:
+ explicit DIR(const char *dirPath) : next(nullptr) {
+ std::string ws = dirPath;
+ if (endsWith(ws, "\\"))
+ ws += "*";
+ else
+ ws += "\\*";
+ hFind = FindFirstFileA(ws.c_str(), &FindFileData);
+ FindFileData.dwReserved0 = hFind != INVALID_HANDLE_VALUE;
+ }
+
+ ~DIR() {
+ if (!next) delete next;
+ FindClose(hFind);
+ }
+
+ bool isValid() const {
+ return (hFind != INVALID_HANDLE_VALUE && FindFileData.dwReserved0);
+ }
+
+ dirent* nextEnt() {
+ if (next != nullptr) delete next;
+ next = nullptr;
+
+ if (!FindFileData.dwReserved0) return nullptr;
+
+ wchar_t wbuf[4096];
+
+ size_t outSize;
+ mbstowcs_s(&outSize, wbuf, 4094, FindFileData.cFileName, 4094);
+ next = new dirent(wbuf);
+ FindFileData.dwReserved0 = FindNextFileA(hFind, &FindFileData);
+ return next;
+ }
+};
+
+
+static DIR *opendir(const char* dirPath) {
+ auto dp = new DIR(dirPath);
+ if (!dp->isValid()) {
+ delete dp;
+ return nullptr;
+ }
+ return dp;
+}
+
+static struct dirent *readdir(DIR *dp) {
+ return dp->nextEnt();
+}
+
+static void closedir(DIR *dp) {
+ delete dp;
+}