summaryrefslogtreecommitdiff
path: root/python/openvino/runtime/common/demo_utils/include/utils
diff options
context:
space:
mode:
Diffstat (limited to 'python/openvino/runtime/common/demo_utils/include/utils')
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/args_helper.hpp43
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/common.hpp190
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/config_factory.h52
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/default_flags.hpp21
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/grid_mat.hpp127
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/image_utils.h29
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/images_capture.h53
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/input_wrappers.hpp149
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/kuhn_munkres.hpp57
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/nms.hpp81
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/ocv_common.hpp289
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/performance_metrics.hpp92
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/shared_tensor_allocator.hpp47
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/slog.hpp99
-rw-r--r--python/openvino/runtime/common/demo_utils/include/utils/threads_common.hpp165
15 files changed, 1494 insertions, 0 deletions
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/args_helper.hpp b/python/openvino/runtime/common/demo_utils/include/utils/args_helper.hpp
new file mode 100644
index 0000000..7a638cc
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/args_helper.hpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2018-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+/**
+ * @brief a header file with common samples functionality
+ * @file args_helper.hpp
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <opencv2/core/types.hpp>
+#include <openvino/openvino.hpp>
+
+/**
+* @brief This function checks input args and existence of specified files in a given folder
+* @param arg path to a file to be checked for existence
+* @return files updated vector of verified input files
+*/
+void readInputFilesArguments(std::vector<std::string>& files, const std::string& arg);
+
+/**
+* @brief This function finds -i/--i key in input args
+* It's necessary to process multiple values for single key
+* @return files updated vector of verified input files
+*/
+void parseInputFilesArguments(std::vector<std::string>& files);
+
+std::vector<std::string> split(const std::string& s, char delim);
+
+std::vector<std::string> parseDevices(const std::string& device_string);
+
+std::map<std::string, int32_t> parseValuePerDevice(const std::set<std::string>& devices,
+ const std::string& values_string);
+
+cv::Size stringToSize(const std::string& str);
+
+std::map<std::string, ov::Layout> parseLayoutString(const std::string& layout_string);
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/common.hpp b/python/openvino/runtime/common/demo_utils/include/utils/common.hpp
new file mode 100644
index 0000000..dbe7cf0
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/common.hpp
@@ -0,0 +1,190 @@
+// Copyright (C) 2018-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+/**
+ * @brief a header file with common samples functionality
+ * @file common.hpp
+ */
+
+#pragma once
+
+#include <iostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <openvino/openvino.hpp>
+#include "utils/slog.hpp"
+#include "utils/args_helper.hpp"
+
+#ifndef UNUSED
+#ifdef _WIN32
+#define UNUSED
+#else
+#define UNUSED __attribute__((unused))
+#endif
+#endif
+
+template <typename T, std::size_t N>
+constexpr std::size_t arraySize(const T(&)[N]) noexcept {
+ return N;
+}
+
+static inline void catcher() noexcept {
+ if (std::current_exception()) {
+ try {
+ std::rethrow_exception(std::current_exception());
+ } catch (const std::exception& error) {
+ slog::err << error.what() << slog::endl;
+ } catch (...) {
+ slog::err << "Non-exception object thrown" << slog::endl;
+ }
+ std::exit(1);
+ }
+ std::abort();
+}
+
+template <typename T>
+T clamp(T value, T low, T high) {
+ return value < low ? low : (value > high ? high : value);
+}
+
+inline slog::LogStream& operator<<(slog::LogStream& os, const ov::Version& version) {
+ return os << "OpenVINO" << slog::endl
+ << "\tversion: " << OPENVINO_VERSION_MAJOR << "." << OPENVINO_VERSION_MINOR << "." << OPENVINO_VERSION_PATCH << slog::endl
+ << "\tbuild: " << version.buildNumber;
+}
+
+/**
+ * @class Color
+ * @brief A Color class stores channels of a given color
+ */
+class Color {
+private:
+ 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() const {
+ return _r;
+ }
+
+ inline unsigned char blue() const {
+ return _b;
+ }
+
+ inline unsigned char green() const {
+ return _g;
+ }
+};
+
+// Known colors for training classes from the Cityscapes dataset
+static UNUSED const Color CITYSCAPES_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 }
+};
+
+inline void showAvailableDevices() {
+ ov::Core core;
+ std::vector<std::string> devices = core.get_available_devices();
+
+ std::cout << "Available devices:";
+ for (const auto& device : devices) {
+ std::cout << ' ' << device;
+ }
+ std::cout << std::endl;
+}
+
+inline std::string fileNameNoExt(const std::string& filepath) {
+ auto pos = filepath.rfind('.');
+ if (pos == std::string::npos) return filepath;
+ return filepath.substr(0, pos);
+}
+
+inline void logCompiledModelInfo(
+ const ov::CompiledModel& compiledModel,
+ const std::string& modelName,
+ const std::string& deviceName,
+ const std::string& modelType = "") {
+ slog::info << "The " << modelType << (modelType.empty() ? "" : " ") << "model " << modelName << " is loaded to " << deviceName << slog::endl;
+ std::set<std::string> devices;
+ for (const std::string& device : parseDevices(deviceName)) {
+ devices.insert(device);
+ }
+
+ if (devices.find("AUTO") == devices.end()) { // do not print info for AUTO device
+ for (const auto& device : devices) {
+ try {
+ slog::info << "\tDevice: " << device << slog::endl;
+ int32_t nstreams = compiledModel.get_property(ov::streams::num);
+ slog::info << "\t\tNumber of streams: " << nstreams << slog::endl;
+ if (device == "CPU") {
+ int32_t nthreads = compiledModel.get_property(ov::inference_num_threads);
+ slog::info << "\t\tNumber of threads: " << (nthreads == 0 ? "AUTO" : std::to_string(nthreads)) << slog::endl;
+ }
+ }
+ catch (const ov::Exception&) {}
+ }
+ }
+}
+
+inline void logBasicModelInfo(const std::shared_ptr<ov::Model>& model) {
+ slog::info << "Model name: " << model->get_friendly_name() << slog::endl;
+
+ // Dump information about model inputs/outputs
+ ov::OutputVector inputs = model->inputs();
+ ov::OutputVector outputs = model->outputs();
+
+ slog::info << "\tInputs: " << slog::endl;
+ for (const ov::Output<ov::Node>& input : inputs) {
+ const std::string name = input.get_any_name();
+ const ov::element::Type type = input.get_element_type();
+ const ov::PartialShape shape = input.get_partial_shape();
+ const ov::Layout layout = ov::layout::get_layout(input);
+
+ slog::info << "\t\t" << name << ", " << type << ", " << shape << ", " << layout.to_string() << slog::endl;
+ }
+
+ slog::info << "\tOutputs: " << slog::endl;
+ for (const ov::Output<ov::Node>& output : outputs) {
+ const std::string name = output.get_any_name();
+ const ov::element::Type type = output.get_element_type();
+ const ov::PartialShape shape = output.get_partial_shape();
+ const ov::Layout layout = ov::layout::get_layout(output);
+
+ slog::info << "\t\t" << name << ", " << type << ", " << shape << ", " << layout.to_string() << slog::endl;
+ }
+
+ return;
+}
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/config_factory.h b/python/openvino/runtime/common/demo_utils/include/utils/config_factory.h
new file mode 100644
index 0000000..c7440b5
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/config_factory.h
@@ -0,0 +1,52 @@
+/*
+// 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.
+*/
+
+#pragma once
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+
+#include <openvino/openvino.hpp>
+
+struct ModelConfig {
+ std::string deviceName;
+ std::string cpuExtensionsPath;
+ std::string clKernelsConfigPath;
+ std::string fpgaArchPath;
+ unsigned int maxAsyncRequests;
+ ov::AnyMap compiledModelConfig;
+
+ std::set<std::string> getDevices();
+ std::map<std::string, std::string> getLegacyConfig();
+
+protected:
+ std::set<std::string> devices;
+};
+
+class ConfigFactory {
+public:
+ static ModelConfig getUserConfig(const std::string& flags_d,
+ uint32_t flags_nireq,
+ const std::string& flags_nstreams,
+ uint32_t flags_nthreads,
+ const std::string &flags_arch);
+ static ModelConfig getMinLatencyConfig(const std::string& flags_d, uint32_t flags_nireq);
+
+protected:
+ static ModelConfig getCommonConfig(const std::string& flags_d, uint32_t flags_nireq);
+};
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/default_flags.hpp b/python/openvino/runtime/common/demo_utils/include/utils/default_flags.hpp
new file mode 100644
index 0000000..83c32c2
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/default_flags.hpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <gflags/gflags.h>
+
+#define DEFINE_INPUT_FLAGS \
+DEFINE_string(i, "", input_message); \
+DEFINE_bool(loop, false, loop_message);
+
+#define DEFINE_OUTPUT_FLAGS \
+DEFINE_string(o, "", output_message); \
+DEFINE_int32(limit, 1000, limit_message);
+
+static const char input_message[] = "Required. An input to process. The input must be a single image, a folder of "
+ "images, video file or camera id.";
+static const char loop_message[] = "Optional. Enable reading the input in a loop.";
+static const char output_message[] = "Optional. Name of the output file(s) to save.";
+static const char limit_message[] = "Optional. Number of frames to store in output. If 0 is set, all frames are stored.";
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/grid_mat.hpp b/python/openvino/runtime/common/demo_utils/include/utils/grid_mat.hpp
new file mode 100644
index 0000000..7d46d2b
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/grid_mat.hpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2018-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <opencv2/core/core.hpp>
+
+class GridMat {
+public:
+ cv::Mat outimg;
+
+ explicit GridMat(const std::vector<cv::Size>& sizes, const cv::Size maxDisp = cv::Size{1920, 1080}) {
+ size_t maxWidth = 0;
+ size_t maxHeight = 0;
+ for (size_t i = 0; i < sizes.size(); i++) {
+ maxWidth = std::max(maxWidth, static_cast<size_t>(sizes[i].width));
+ maxHeight = std::max(maxHeight, static_cast<size_t>(sizes[i].height));
+ }
+ if (0 == maxWidth || 0 == maxHeight) {
+ throw std::invalid_argument("Input resolution must not be zero.");
+ }
+
+ size_t nGridCols = static_cast<size_t>(ceil(sqrt(static_cast<float>(sizes.size()))));
+ size_t nGridRows = (sizes.size() - 1) / nGridCols + 1;
+ size_t gridMaxWidth = static_cast<size_t>(maxDisp.width/nGridCols);
+ size_t gridMaxHeight = static_cast<size_t>(maxDisp.height/nGridRows);
+
+ float scaleWidth = static_cast<float>(gridMaxWidth) / maxWidth;
+ float scaleHeight = static_cast<float>(gridMaxHeight) / maxHeight;
+ float scaleFactor = std::min(1.f, std::min(scaleWidth, scaleHeight));
+
+ cellSize.width = static_cast<int>(maxWidth * scaleFactor);
+ cellSize.height = static_cast<int>(maxHeight * scaleFactor);
+
+ for (size_t i = 0; i < sizes.size(); i++) {
+ cv::Point p;
+ p.x = cellSize.width * (i % nGridCols);
+ p.y = cellSize.height * (i / nGridCols);
+ points.push_back(p);
+ }
+
+ outimg.create(cellSize.height * nGridRows, cellSize.width * nGridCols, CV_8UC3);
+ outimg.setTo(0);
+ clear();
+ }
+
+ cv::Size getCellSize() {
+ return cellSize;
+ }
+
+ void fill(std::vector<cv::Mat>& frames) {
+ if (frames.size() > points.size()) {
+ throw std::logic_error("Cannot display " + std::to_string(frames.size()) + " channels in a grid with " + std::to_string(points.size()) + " cells");
+ }
+
+ for (size_t i = 0; i < frames.size(); i++) {
+ cv::Mat cell = outimg(cv::Rect(points[i].x, points[i].y, cellSize.width, cellSize.height));
+
+ if ((cellSize.width == frames[i].cols) && (cellSize.height == frames[i].rows)) {
+ frames[i].copyTo(cell);
+ } else if ((cellSize.width > frames[i].cols) && (cellSize.height > frames[i].rows)) {
+ frames[i].copyTo(cell(cv::Rect(0, 0, frames[i].cols, frames[i].rows)));
+ } else {
+ cv::resize(frames[i], cell, cellSize);
+ }
+ }
+ unupdatedSourceIDs.clear();
+ }
+
+ void update(const cv::Mat& frame, const size_t sourceID) {
+ const cv::Mat& cell = outimg(cv::Rect(points[sourceID], cellSize));
+
+ if ((cellSize.width == frame.cols) && (cellSize.height == frame.rows)) {
+ frame.copyTo(cell);
+ } else if ((cellSize.width > frame.cols) && (cellSize.height > frame.rows)) {
+ frame.copyTo(cell(cv::Rect(0, 0, frame.cols, frame.rows)));
+ } else {
+ cv::resize(frame, cell, cellSize);
+ }
+ unupdatedSourceIDs.erase(unupdatedSourceIDs.find(sourceID));
+ }
+
+ bool isFilled() const noexcept {
+ return unupdatedSourceIDs.empty();
+ }
+ void clear() {
+ size_t counter = 0;
+ std::generate_n(std::inserter(unupdatedSourceIDs, unupdatedSourceIDs.end()), points.size(), [&counter]{return counter++;});
+ }
+ std::set<size_t> getUnupdatedSourceIDs() const noexcept {
+ return unupdatedSourceIDs;
+ }
+ cv::Mat getMat() const noexcept {
+ return outimg;
+ }
+
+private:
+ cv::Size cellSize;
+ std::set<size_t> unupdatedSourceIDs;
+ std::vector<cv::Point> points;
+};
+
+void fillROIColor(cv::Mat& displayImage, cv::Rect roi, cv::Scalar color, double opacity) {
+ if (opacity > 0) {
+ roi = roi & cv::Rect(0, 0, displayImage.cols, displayImage.rows);
+ cv::Mat textROI = displayImage(roi);
+ cv::addWeighted(color, opacity, textROI, 1.0 - opacity , 0.0, textROI);
+ }
+}
+
+void putTextOnImage(cv::Mat& displayImage, std::string str, cv::Point p,
+ cv::HersheyFonts font, double fontScale, cv::Scalar color,
+ int thickness = 1, cv::Scalar bgcolor = cv::Scalar(),
+ double opacity = 0) {
+ int baseline = 0;
+ cv::Size textSize = cv::getTextSize(str, font, 0.5, 1, &baseline);
+ fillROIColor(displayImage, cv::Rect(cv::Point(p.x, p.y + baseline),
+ cv::Point(p.x + textSize.width, p.y - textSize.height)),
+ bgcolor, opacity);
+ cv::putText(displayImage, str, p, font, fontScale, color, thickness);
+}
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/image_utils.h b/python/openvino/runtime/common/demo_utils/include/utils/image_utils.h
new file mode 100644
index 0000000..2731a9a
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/image_utils.h
@@ -0,0 +1,29 @@
+/*
+// 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.
+*/
+
+#pragma once
+
+#include <opencv2/opencv.hpp>
+
+enum RESIZE_MODE {
+ RESIZE_FILL,
+ RESIZE_KEEP_ASPECT,
+ RESIZE_KEEP_ASPECT_LETTERBOX
+};
+
+cv::Mat resizeImageExt(const cv::Mat& mat, int width, int height, RESIZE_MODE resizeMode = RESIZE_FILL,
+ cv::InterpolationFlags interpolationMode = cv::INTER_LINEAR, cv::Rect* roi = nullptr,
+ cv::Scalar BorderConstant = cv::Scalar(0, 0, 0));
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/images_capture.h b/python/openvino/runtime/common/demo_utils/include/utils/images_capture.h
new file mode 100644
index 0000000..f2afdfc
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/images_capture.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2020-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+#include <stddef.h>
+
+#include <limits>
+#include <memory>
+#include <string>
+
+#include <opencv2/core.hpp>
+
+#include "utils/performance_metrics.hpp"
+
+enum class read_type { efficient, safe };
+
+class ImagesCapture {
+public:
+ const bool loop;
+
+ ImagesCapture(bool loop) : loop{loop} {}
+ virtual double fps() const = 0;
+ virtual cv::Mat read() = 0;
+ virtual std::string getType() const = 0;
+ const PerformanceMetrics& getMetrics() {
+ return readerMetrics;
+ }
+ virtual ~ImagesCapture() = default;
+
+protected:
+ PerformanceMetrics readerMetrics;
+};
+
+// An advanced version of
+// try {
+// return cv::VideoCapture(std::stoi(input));
+// } catch (const std::invalid_argument&) {
+// return cv::VideoCapture(input);
+// } catch (const std::out_of_range&) {
+// return cv::VideoCapture(input);
+// }
+// Some VideoCapture backends continue owning the video buffer under cv::Mat. safe_copy forses to return a copy from
+// read()
+// https://github.com/opencv/opencv/blob/46e1560678dba83d25d309d8fbce01c40f21b7be/modules/gapi/include/opencv2/gapi/streaming/cap.hpp#L72-L76
+std::unique_ptr<ImagesCapture> openImagesCapture(
+ const std::string& input,
+ bool loop,
+ read_type type = read_type::efficient,
+ size_t initialImageId = 0,
+ size_t readLengthLimit = std::numeric_limits<size_t>::max(), // General option
+ cv::Size cameraResolution = {1280, 720}
+ );
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/input_wrappers.hpp b/python/openvino/runtime/common/demo_utils/include/utils/input_wrappers.hpp
new file mode 100644
index 0000000..eff38a7
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/input_wrappers.hpp
@@ -0,0 +1,149 @@
+// Copyright (C) 2018-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <set>
+#include <thread>
+#include <vector>
+#include <queue>
+
+#include <opencv2/opencv.hpp>
+
+class InputChannel;
+
+class IInputSource {
+public:
+ virtual bool read(cv::Mat& mat, const std::shared_ptr<InputChannel>& caller) = 0;
+ virtual void addSubscriber(const std::weak_ptr<InputChannel>& inputChannel) = 0;
+ virtual cv::Size getSize() = 0;
+ virtual void lock() {
+ sourceLock.lock();
+ }
+ virtual void unlock() {
+ sourceLock.unlock();
+ }
+ virtual ~IInputSource() = default;
+private:
+ std::mutex sourceLock;
+};
+
+class InputChannel: public std::enable_shared_from_this<InputChannel> { // note: public inheritance
+public:
+ InputChannel(const InputChannel&) = delete;
+ InputChannel& operator=(const InputChannel&) = delete;
+ static std::shared_ptr<InputChannel> create(const std::shared_ptr<IInputSource>& source) {
+ auto tmp = std::shared_ptr<InputChannel>(new InputChannel(source));
+ source->addSubscriber(tmp);
+ return tmp;
+ }
+ bool read(cv::Mat& mat) {
+ readQueueMutex.lock();
+ if (readQueue.empty()) {
+ readQueueMutex.unlock();
+ source->lock();
+ readQueueMutex.lock();
+ if (readQueue.empty()) {
+ bool res = source->read(mat, shared_from_this());
+ readQueueMutex.unlock();
+ source->unlock();
+ return res;
+ } else {
+ source->unlock();
+ }
+ }
+ mat = readQueue.front().clone();
+ readQueue.pop();
+ readQueueMutex.unlock();
+ return true;
+ }
+ void push(const cv::Mat& mat) {
+ readQueueMutex.lock();
+ readQueue.push(mat);
+ readQueueMutex.unlock();
+ }
+ cv::Size getSize() {
+ return source->getSize();
+ }
+
+private:
+ explicit InputChannel(const std::shared_ptr<IInputSource>& source): source{source} {}
+ std::shared_ptr<IInputSource> source;
+ std::queue<cv::Mat, std::list<cv::Mat>> readQueue;
+ std::mutex readQueueMutex;
+};
+
+class VideoCaptureSource: public IInputSource {
+public:
+ VideoCaptureSource(const cv::VideoCapture& videoCapture, bool loop): videoCapture{videoCapture}, loop{loop},
+ imSize{static_cast<int>(videoCapture.get(cv::CAP_PROP_FRAME_WIDTH)), static_cast<int>(videoCapture.get(cv::CAP_PROP_FRAME_HEIGHT))} {}
+ bool read(cv::Mat& mat, const std::shared_ptr<InputChannel>& caller) override {
+ if (!videoCapture.read(mat)) {
+ if (loop) {
+ videoCapture.set(cv::CAP_PROP_POS_FRAMES, 0);
+ videoCapture.read(mat);
+ } else {
+ return false;
+ }
+ }
+ if (1 != subscribedInputChannels.size()) {
+ cv::Mat shared = mat.clone();
+ for (const std::weak_ptr<InputChannel>& weakInputChannel : subscribedInputChannels) {
+ try {
+ std::shared_ptr<InputChannel> sharedInputChannel = std::shared_ptr<InputChannel>(weakInputChannel);
+ if (caller != sharedInputChannel) {
+ sharedInputChannel->push(shared);
+ }
+ } catch (const std::bad_weak_ptr&) {}
+ }
+ }
+ return true;
+ }
+ void addSubscriber(const std::weak_ptr<InputChannel>& inputChannel) override {
+ subscribedInputChannels.push_back(inputChannel);
+ }
+ cv::Size getSize() override {
+ return imSize;
+ }
+
+private:
+ std::vector<std::weak_ptr<InputChannel>> subscribedInputChannels;
+ cv::VideoCapture videoCapture;
+ bool loop;
+ cv::Size imSize;
+};
+
+class ImageSource: public IInputSource {
+public:
+ ImageSource(const cv::Mat& im, bool loop): im{im.clone()}, loop{loop} {} // clone to avoid image changing
+ bool read(cv::Mat& mat, const std::shared_ptr<InputChannel>& caller) override {
+ if (!loop) {
+ auto subscribedInputChannelsIt = subscribedInputChannels.find(caller);
+ if (subscribedInputChannels.end() == subscribedInputChannelsIt) {
+ return false;
+ } else {
+ subscribedInputChannels.erase(subscribedInputChannelsIt);
+ mat = im;
+ return true;
+ }
+ } else {
+ mat = im;
+ return true;
+ }
+ }
+ void addSubscriber(const std::weak_ptr<InputChannel>& inputChannel) override {
+ if (false == subscribedInputChannels.insert(inputChannel).second)
+ throw std::invalid_argument("The insertion did not take place");
+ }
+ cv::Size getSize() override {
+ return im.size();
+ }
+
+private:
+ std::set<std::weak_ptr<InputChannel>, std::owner_less<std::weak_ptr<InputChannel>>> subscribedInputChannels;
+ cv::Mat im;
+ bool loop;
+};
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/kuhn_munkres.hpp b/python/openvino/runtime/common/demo_utils/include/utils/kuhn_munkres.hpp
new file mode 100644
index 0000000..6e6ac51
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/kuhn_munkres.hpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2018-2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include "opencv2/core.hpp"
+
+#include <memory>
+#include <vector>
+
+
+///
+/// \brief The KuhnMunkres class
+///
+/// Solves the assignment problem.
+///
+class KuhnMunkres {
+public:
+ ///
+ /// \brief Initializes the class for assignment problem solving.
+ /// \param[in] greedy If a faster greedy matching algorithm should be used.
+ explicit KuhnMunkres(bool greedy = false);
+
+ ///
+ /// \brief Solves the assignment problem for given dissimilarity matrix.
+ /// It returns a vector that where each element is a column index for
+ /// corresponding row (e.g. result[0] stores optimal column index for very
+ /// first row in the dissimilarity matrix).
+ /// \param dissimilarity_matrix CV_32F dissimilarity matrix.
+ /// \return Optimal column index for each row. -1 means that there is no
+ /// column for row.
+ ///
+ std::vector<size_t> Solve(const cv::Mat &dissimilarity_matrix);
+
+private:
+ static constexpr int kStar = 1;
+ static constexpr int kPrime = 2;
+
+ cv::Mat dm_;
+ cv::Mat marked_;
+ std::vector<cv::Point> points_;
+
+ std::vector<int> is_row_visited_;
+ std::vector<int> is_col_visited_;
+
+ int n_;
+ bool greedy_;
+
+ void TrySimpleCase();
+ bool CheckIfOptimumIsFound();
+ cv::Point FindUncoveredMinValPos();
+ void UpdateDissimilarityMatrix(float val);
+ int FindInRow(int row, int what);
+ int FindInCol(int col, int what);
+ void Run();
+};
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/nms.hpp b/python/openvino/runtime/common/demo_utils/include/utils/nms.hpp
new file mode 100644
index 0000000..1fd475f
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/nms.hpp
@@ -0,0 +1,81 @@
+/*
+// 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.
+*/
+
+#pragma once
+
+#include "opencv2/core.hpp"
+#include <numeric>
+#include <vector>
+
+struct Anchor {
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ float getWidth() const {
+ return (right - left) + 1.0f;
+ }
+ float getHeight() const {
+ return (bottom - top) + 1.0f;
+ }
+ float getXCenter() const {
+ return left + (getWidth() - 1.0f) / 2.0f;
+ }
+ float getYCenter() const {
+ return top + (getHeight() - 1.0f) / 2.0f;
+ }
+};
+
+template <typename Anchor>
+std::vector<int> nms(const std::vector<Anchor>& boxes, const std::vector<float>& scores,
+ const float thresh, bool includeBoundaries=false) {
+ std::vector<float> areas(boxes.size());
+ for (size_t i = 0; i < boxes.size(); ++i) {
+ areas[i] = (boxes[i].right - boxes[i].left + includeBoundaries) * (boxes[i].bottom - boxes[i].top + includeBoundaries);
+ }
+ std::vector<int> order(scores.size());
+ std::iota(order.begin(), order.end(), 0);
+ std::sort(order.begin(), order.end(), [&scores](int o1, int o2) { return scores[o1] > scores[o2]; });
+
+ size_t ordersNum = 0;
+ for (; ordersNum < order.size() && scores[order[ordersNum]] >= 0; ordersNum++);
+
+ std::vector<int> keep;
+ bool shouldContinue = true;
+ for (size_t i = 0; shouldContinue && i < ordersNum; ++i) {
+ auto idx1 = order[i];
+ if (idx1 >= 0) {
+ keep.push_back(idx1);
+ shouldContinue = false;
+ for (size_t j = i + 1; j < ordersNum; ++j) {
+ auto idx2 = order[j];
+ if (idx2 >= 0) {
+ shouldContinue = true;
+ auto overlappingWidth = std::fminf(boxes[idx1].right, boxes[idx2].right) - std::fmaxf(boxes[idx1].left, boxes[idx2].left);
+ auto overlappingHeight = std::fminf(boxes[idx1].bottom, boxes[idx2].bottom) - std::fmaxf(boxes[idx1].top, boxes[idx2].top);
+ auto intersection = overlappingWidth > 0 && overlappingHeight > 0 ? overlappingWidth * overlappingHeight : 0;
+ auto overlap = intersection / (areas[idx1] + areas[idx2] - intersection);
+
+ if (overlap >= thresh) {
+ order[j] = -1;
+ }
+ }
+ }
+ }
+ }
+ return keep;
+}
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/ocv_common.hpp b/python/openvino/runtime/common/demo_utils/include/utils/ocv_common.hpp
new file mode 100644
index 0000000..ebb5e14
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/ocv_common.hpp
@@ -0,0 +1,289 @@
+// Copyright (C) 2018-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+/**
+ * @brief a header file with common samples functionality using OpenCV
+ * @file ocv_common.hpp
+ */
+
+#pragma once
+
+#include <opencv2/opencv.hpp>
+#include <openvino/openvino.hpp>
+
+#include "utils/common.hpp"
+#include "utils/shared_tensor_allocator.hpp"
+
+/**
+* @brief Get cv::Mat value in the correct format.
+*/
+template <typename T>
+const T getMatValue(const cv::Mat& mat, size_t h, size_t w, size_t c) {
+ switch (mat.type()) {
+ case CV_8UC1: return (T)mat.at<uchar>(h, w);
+ case CV_8UC3: return (T)mat.at<cv::Vec3b>(h, w)[c];
+ case CV_32FC1: return (T)mat.at<float>(h, w);
+ case CV_32FC3: return (T)mat.at<cv::Vec3f>(h, w)[c];
+ }
+ throw std::runtime_error("cv::Mat type is not recognized");
+};
+
+/**
+* @brief Resize and copy image data from cv::Mat object to a given Tensor object.
+* @param mat - given cv::Mat object with an image data.
+* @param tensor - Tensor object which to be filled by an image data.
+* @param batchIndex - batch index of an image inside of the blob.
+*/
+static UNUSED void matToTensor(const cv::Mat& mat, const ov::Tensor& tensor, int batchIndex = 0) {
+ ov::Shape tensorShape = tensor.get_shape();
+ static const ov::Layout layout("NCHW");
+ const size_t width = tensorShape[ov::layout::width_idx(layout)];
+ const size_t height = tensorShape[ov::layout::height_idx(layout)];
+ const size_t channels = tensorShape[ov::layout::channels_idx(layout)];
+ if (static_cast<size_t>(mat.channels()) != channels) {
+ throw std::runtime_error("The number of channels for model input and image must match");
+ }
+ if (channels != 1 && channels != 3) {
+ throw std::runtime_error("Unsupported number of channels");
+ }
+ int batchOffset = batchIndex * width * height * channels;
+
+ cv::Mat resizedMat;
+ if (static_cast<int>(width) != mat.size().width || static_cast<int>(height) != mat.size().height) {
+ cv::resize(mat, resizedMat, cv::Size(width, height));
+ } else {
+ resizedMat = mat;
+ }
+
+ if (tensor.get_element_type() == ov::element::f32) {
+ float_t* tensorData = tensor.data<float_t>();
+ for (size_t c = 0; c < channels; c++)
+ for (size_t h = 0; h < height; h++)
+ for (size_t w = 0; w < width; w++)
+ tensorData[batchOffset + c * width * height + h * width + w] =
+ getMatValue<float_t>(resizedMat, h, w, c);
+ } else {
+ uint8_t* tensorData = tensor.data<uint8_t>();
+ if (resizedMat.depth() == CV_32F) {
+ throw std::runtime_error("Conversion of cv::Mat from float_t to uint8_t is forbidden");
+ }
+ for (size_t c = 0; c < channels; c++)
+ for (size_t h = 0; h < height; h++)
+ for (size_t w = 0; w < width; w++)
+ tensorData[batchOffset + c * width * height + h * width + w] =
+ getMatValue<uint8_t>(resizedMat, h, w, c);
+ }
+}
+
+static UNUSED ov::Tensor wrapMat2Tensor(const cv::Mat& mat) {
+ auto matType = mat.type() & CV_MAT_DEPTH_MASK;
+ if (matType != CV_8U && matType != CV_32F) {
+ throw std::runtime_error("Unsupported mat type for wrapping");
+ }
+ bool isMatFloat = matType == CV_32F;
+
+ const size_t channels = mat.channels();
+ const size_t height = mat.rows;
+ const size_t width = mat.cols;
+
+ const size_t strideH = mat.step.buf[0];
+ const size_t strideW = mat.step.buf[1];
+
+ const bool isDense = !isMatFloat ? (strideW == channels && strideH == channels * width) :
+ (strideW == channels * sizeof(float) && strideH == channels * width * sizeof(float));
+ if (!isDense) {
+ throw std::runtime_error("Doesn't support conversion from not dense cv::Mat");
+ }
+ auto precision = isMatFloat ? ov::element::f32 : ov::element::u8;
+ auto allocator = std::make_shared<SharedTensorAllocator>(mat);
+ return ov::Tensor(precision, ov::Shape{ 1, height, width, channels }, ov::Allocator(allocator));
+}
+
+static inline void resize2tensor(const cv::Mat& mat, const ov::Tensor& tensor) {
+ static const ov::Layout layout{"NHWC"};
+ const ov::Shape& shape = tensor.get_shape();
+ cv::Size size{int(shape[ov::layout::width_idx(layout)]), int(shape[ov::layout::height_idx(layout)])};
+ assert(tensor.get_element_type() == ov::element::u8);
+ assert(shape.size() == 4);
+ assert(shape[ov::layout::batch_idx(layout)] == 1);
+ assert(shape[ov::layout::channels_idx(layout)] == 3);
+ cv::resize(mat, cv::Mat{size, CV_8UC3, tensor.data()}, size);
+}
+
+static inline ov::Layout getLayoutFromShape(const ov::Shape& shape) {
+ if (shape.size() == 2) {
+ return "NC";
+ }
+ else if (shape.size() == 3) {
+ return (shape[0] >= 1 && shape[0] <= 4) ? "CHW" :
+ "HWC";
+ }
+ else if (shape.size() == 4) {
+ return (shape[1] >= 1 && shape[1] <= 4) ? "NCHW" :
+ "NHWC";
+ }
+ else {
+ throw std::runtime_error("Usupported " + std::to_string(shape.size()) + "D shape");
+ }
+}
+
+/**
+ * @brief Puts text message on the frame, highlights the text with a white border to make it distinguishable from
+ * the background.
+ * @param frame - frame to put the text on.
+ * @param message - text of the message.
+ * @param position - bottom-left corner of the text string in the image.
+ * @param fontFace - font type.
+ * @param fontScale - font scale factor that is multiplied by the font-specific base size.
+ * @param color - text color.
+ * @param thickness - thickness of the lines used to draw a text.
+ */
+inline void putHighlightedText(const cv::Mat& frame,
+ const std::string& message,
+ cv::Point position,
+ int fontFace,
+ double fontScale,
+ cv::Scalar color,
+ int thickness) {
+ cv::putText(frame, message, position, fontFace, fontScale, cv::Scalar(255, 255, 255), thickness + 1);
+ cv::putText(frame, message, position, fontFace, fontScale, color, thickness);
+}
+
+// TODO: replace with Size::empty() after OpenCV3 is dropped
+static inline bool isSizeEmpty(const cv::Size& size) {
+ return size.width <= 0 || size.height <= 0;
+}
+
+// TODO: replace with Rect::empty() after OpenCV3 is dropped
+static inline bool isRectEmpty(const cv::Rect& rect) {
+ return rect.width <= 0 || rect.height <= 0;
+}
+
+class OutputTransform {
+public:
+ OutputTransform() : doResize(false), scaleFactor(1) {}
+
+ OutputTransform(cv::Size inputSize, cv::Size outputResolution) :
+ doResize(true), scaleFactor(1), inputSize(inputSize), outputResolution(outputResolution) {}
+
+ cv::Size computeResolution() {
+ float inputWidth = static_cast<float>(inputSize.width);
+ float inputHeight = static_cast<float>(inputSize.height);
+ scaleFactor = std::min(outputResolution.height / inputHeight, outputResolution.width / inputWidth);
+ newResolution = cv::Size{static_cast<int>(inputWidth * scaleFactor), static_cast<int>(inputHeight * scaleFactor)};
+ return newResolution;
+ }
+
+ void resize(cv::Mat& image) {
+ if (!doResize) { return; }
+ cv::Size currSize = image.size();
+ if (currSize != inputSize) {
+ inputSize = currSize;
+ computeResolution();
+ }
+ if (scaleFactor == 1) { return; }
+ cv::resize(image, image, newResolution);
+ }
+
+ template<typename T>
+ void scaleCoord(T& coord) {
+ if (!doResize || scaleFactor == 1) { return; }
+ coord.x = std::floor(coord.x * scaleFactor);
+ coord.y = std::floor(coord.y * scaleFactor);
+ }
+
+ template<typename T>
+ void scaleRect(T& rect) {
+ if (!doResize || scaleFactor == 1) { return; }
+ scaleCoord(rect);
+ rect.width = std::floor(rect.width * scaleFactor);
+ rect.height = std::floor(rect.height * scaleFactor);
+ }
+
+ bool doResize;
+
+private:
+ float scaleFactor;
+ cv::Size inputSize;
+ cv::Size outputResolution;
+ cv::Size newResolution;
+};
+
+class InputTransform {
+public:
+ InputTransform() : reverseInputChannels(false), isTrivial(true) {}
+
+ InputTransform(bool reverseInputChannels, const std::string& meanValues, const std::string& scaleValues) :
+ reverseInputChannels(reverseInputChannels),
+ isTrivial(!reverseInputChannels && meanValues.empty() && scaleValues.empty()),
+ means(meanValues.empty() ? cv::Scalar(0.0, 0.0, 0.0) : string2Vec(meanValues)),
+ stdScales(scaleValues.empty() ? cv::Scalar(1.0, 1.0, 1.0) : string2Vec(scaleValues)) {
+ }
+
+ cv::Scalar string2Vec(const std::string& string) {
+ const auto& strValues = split(string, ' ');
+ std::vector<float> values;
+ try {
+ for (auto& str : strValues)
+ values.push_back(std::stof(str));
+ }
+ catch (const std::invalid_argument&) {
+ throw std::runtime_error("Invalid parameter --mean_values or --scale_values is provided.");
+ }
+ if (values.size() != 3) {
+ throw std::runtime_error("InputTransform expects 3 values per channel, but get \"" + string + "\".");
+ }
+ return cv::Scalar(values[0], values[1], values[2]);
+ }
+
+ void setPrecision(ov::preprocess::PrePostProcessor& ppp, const std::string& tensorName) {
+ const auto precision = isTrivial ? ov::element::u8 : ov::element::f32;
+ ppp.input(tensorName).tensor().
+ set_element_type(precision);
+ }
+
+ cv::Mat operator()(const cv::Mat& inputs) {
+ if (isTrivial) { return inputs; }
+ cv::Mat result;
+ inputs.convertTo(result, CV_32F);
+ if (reverseInputChannels) {
+ cv::cvtColor(result, result, cv::COLOR_BGR2RGB);
+ }
+ // TODO: merge the two following lines after OpenCV3 is droppped
+ result -= means;
+ result /= cv::Mat{stdScales};
+ return result;
+ }
+
+private:
+ bool reverseInputChannels;
+ bool isTrivial;
+ cv::Scalar means;
+ cv::Scalar stdScales;
+};
+
+class LazyVideoWriter {
+ cv::VideoWriter writer;
+ unsigned nwritten;
+public:
+ const std::string filenames;
+ const double fps;
+ const unsigned lim;
+
+ LazyVideoWriter(const std::string& filenames, double fps, unsigned lim) :
+ nwritten{1}, filenames{filenames}, fps{fps}, lim{lim} {}
+ void write(const cv::Mat& im) {
+ if (writer.isOpened() && (nwritten < lim || 0 == lim)) {
+ writer.write(im);
+ ++nwritten;
+ return;
+ }
+ if (!writer.isOpened() && !filenames.empty()) {
+ if (!writer.open(filenames, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, im.size())) {
+ throw std::runtime_error("Can't open video writer");
+ }
+ writer.write(im);
+ }
+ }
+};
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/performance_metrics.hpp b/python/openvino/runtime/common/demo_utils/include/utils/performance_metrics.hpp
new file mode 100644
index 0000000..6c728b0
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/performance_metrics.hpp
@@ -0,0 +1,92 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+/**
+ * @brief a header file for performance metrics calculation class
+ * @file performance_metrics.hpp
+ */
+
+#pragma once
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "utils/ocv_common.hpp"
+
+class PerformanceMetrics {
+public:
+ using Clock = std::chrono::steady_clock;
+ using TimePoint = std::chrono::time_point<Clock>;
+ using Duration = Clock::duration;
+ using Ms = std::chrono::duration<double, std::ratio<1, 1000>>;
+ using Sec = std::chrono::duration<double, std::ratio<1, 1>>;
+
+ struct Metrics {
+ double latency;
+ double fps;
+ };
+
+ enum MetricTypes {
+ ALL,
+ FPS,
+ LATENCY
+ };
+
+ PerformanceMetrics(Duration timeWindow = std::chrono::seconds(1));
+ void update(TimePoint lastRequestStartTime,
+ const cv::Mat& frame,
+ cv::Point position = {15, 30},
+ int fontFace = cv::FONT_HERSHEY_COMPLEX,
+ double fontScale = 0.75,
+ cv::Scalar color = {200, 10, 10},
+ int thickness = 2, MetricTypes metricType = ALL);
+ void update(TimePoint lastRequestStartTime);
+
+ /// Paints metrics over provided mat
+ /// @param frame frame to paint over
+ /// @param position left top corner of text block
+ /// @param fontScale font scale
+ /// @param color font color
+ /// @param thickness font thickness
+ void paintMetrics(const cv::Mat& frame,
+ cv::Point position = { 15, 30 },
+ int fontFace = cv::FONT_HERSHEY_COMPLEX,
+ double fontScale = 0.75,
+ cv::Scalar color = { 200, 10, 10 },
+ int thickness = 2, MetricTypes metricType = ALL) const;
+
+ Metrics getLast() const;
+ Metrics getTotal() const;
+ void logTotal() const;
+
+private:
+ struct Statistic {
+ Duration latency;
+ Duration period;
+ int frameCount;
+
+ Statistic() {
+ latency = Duration::zero();
+ period = Duration::zero();
+ frameCount = 0;
+ }
+
+ void combine(const Statistic& other) {
+ latency += other.latency;
+ period += other.period;
+ frameCount += other.frameCount;
+ }
+ };
+
+ Duration timeWindowSize;
+ Statistic lastMovingStatistic;
+ Statistic currentMovingStatistic;
+ Statistic totalStatistic;
+ TimePoint lastUpdateTime;
+ bool firstFrameProcessed;
+};
+
+void logLatencyPerStage(double readLat, double preprocLat, double inferLat, double postprocLat, double renderLat);
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/shared_tensor_allocator.hpp b/python/openvino/runtime/common/demo_utils/include/utils/shared_tensor_allocator.hpp
new file mode 100644
index 0000000..f74e8d0
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/shared_tensor_allocator.hpp
@@ -0,0 +1,47 @@
+/*
+// 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.
+*/
+
+#pragma once
+
+#include <opencv2/core.hpp>
+#include <openvino/runtime/allocator.hpp>
+
+// To prevent false-positive clang compiler warning
+// (https://github.com/openvinotoolkit/openvino/pull/11092#issuecomment-1073846256):
+// warning: destructor called on non-final 'SharedTensorAllocator' that has virtual functions
+// but non-virtual destructor [-Wdelete-non-abstract-non-virtual-dtor]
+// SharedTensorAllocator class declared as final
+
+class SharedTensorAllocator final : public ov::AllocatorImpl {
+public:
+ SharedTensorAllocator(const cv::Mat& img) : img(img) {}
+
+ ~SharedTensorAllocator() = default;
+
+ void* allocate(const size_t bytes, const size_t) override {
+ return bytes <= img.rows * img.step[0] ? img.data : nullptr;
+ }
+
+ void deallocate(void* handle, const size_t bytes, const size_t) override {}
+
+ bool is_equal(const AllocatorImpl& other) const override {
+ auto other_tensor_allocator = dynamic_cast<const SharedTensorAllocator*>(&other);
+ return other_tensor_allocator != nullptr && other_tensor_allocator == this;
+ }
+
+private:
+ const cv::Mat img;
+};
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/slog.hpp b/python/openvino/runtime/common/demo_utils/include/utils/slog.hpp
new file mode 100644
index 0000000..316b98d
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/slog.hpp
@@ -0,0 +1,99 @@
+// Copyright (C) 2018-2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+/**
+ * @brief a header file with logging facility for common samples
+ * @file log.hpp
+ */
+
+#pragma once
+
+#include <iostream>
+#include <string>
+
+namespace slog {
+
+/**
+ * @class LogStreamEndLine
+ * @brief The LogStreamEndLine class implements an end line marker for a log stream
+ */
+class LogStreamEndLine { };
+
+static constexpr LogStreamEndLine endl;
+
+
+/**
+ * @class LogStreamBoolAlpha
+ * @brief The LogStreamBoolAlpha class implements bool printing for a log stream
+ */
+class LogStreamBoolAlpha { };
+
+static constexpr LogStreamBoolAlpha boolalpha;
+
+
+/**
+ * @class LogStream
+ * @brief The LogStream class implements a stream for sample logging
+ */
+class LogStream {
+ std::string _prefix;
+ std::ostream* _log_stream;
+ bool _new_line;
+
+public:
+ /**
+ * @brief A constructor. Creates a LogStream object
+ * @param prefix The prefix to print
+ */
+ LogStream(const std::string &prefix, std::ostream& log_stream)
+ : _prefix(prefix), _new_line(true) {
+ _log_stream = &log_stream;
+ }
+
+ /**
+ * @brief A stream output operator to be used within the logger
+ * @param arg Object for serialization in the logger message
+ */
+ template<class T>
+ LogStream &operator<<(const T &arg) {
+ if (_new_line) {
+ (*_log_stream) << "[ " << _prefix << " ] ";
+ _new_line = false;
+ }
+
+ (*_log_stream) << arg;
+ return *this;
+ }
+
+ // Specializing for LogStreamEndLine to support slog::endl
+ LogStream& operator<< (const LogStreamEndLine &/*arg*/) {
+ _new_line = true;
+
+ (*_log_stream) << std::endl;
+ return *this;
+ }
+
+ // Specializing for LogStreamBoolAlpha to support slog::boolalpha
+ LogStream& operator<< (const LogStreamBoolAlpha &/*arg*/) {
+ (*_log_stream) << std::boolalpha;
+ return *this;
+ }
+
+ // Specializing for std::vector and std::list
+ template<template<class, class> class Container, class T>
+ LogStream& operator<< (const Container<T, std::allocator<T>>& container) {
+ for (const auto& el : container) {
+ *this << el << slog::endl;
+ }
+ return *this;
+ }
+};
+
+
+static LogStream info("INFO", std::cout);
+static LogStream debug("DEBUG", std::cout);
+static LogStream warn("WARNING", std::cout);
+static LogStream err("ERROR", std::cerr);
+
+} // namespace slog
diff --git a/python/openvino/runtime/common/demo_utils/include/utils/threads_common.hpp b/python/openvino/runtime/common/demo_utils/include/utils/threads_common.hpp
new file mode 100644
index 0000000..f0e5cbf
--- /dev/null
+++ b/python/openvino/runtime/common/demo_utils/include/utils/threads_common.hpp
@@ -0,0 +1,165 @@
+// Copyright (C) 2018-2022 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <set>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <opencv2/core/core.hpp>
+#include "utils/performance_metrics.hpp"
+
+// VideoFrame can represent not a single image but the whole grid
+class VideoFrame {
+public:
+ typedef std::shared_ptr<VideoFrame> Ptr;
+
+ VideoFrame(unsigned sourceID, int64_t frameId, const cv::Mat& frame = cv::Mat()) :
+ sourceID{sourceID}, frameId{frameId}, frame{frame} {}
+ virtual ~VideoFrame() = default; // A user has to define how it is reconstructed
+
+ const unsigned sourceID;
+ const int64_t frameId;
+ cv::Mat frame;
+
+ PerformanceMetrics::TimePoint timestamp;
+};
+
+class Worker;
+
+class Task {
+public:
+ explicit Task(VideoFrame::Ptr sharedVideoFrame, float priority = 0):
+ sharedVideoFrame{sharedVideoFrame}, priority{priority} {}
+ virtual bool isReady() = 0;
+ virtual void process() = 0;
+ virtual ~Task() = default;
+
+ std::string name;
+ VideoFrame::Ptr sharedVideoFrame; // it is possible that two tasks try to draw on the same cvMat
+ const float priority;
+};
+
+struct HigherPriority {
+ bool operator()(const std::shared_ptr<Task>& lhs, const std::shared_ptr<Task>& rhs) const {
+ return lhs->priority > rhs->priority
+ || (lhs->priority == rhs->priority && lhs->sharedVideoFrame->frameId < rhs->sharedVideoFrame->frameId)
+ || (lhs->priority == rhs->priority && lhs->sharedVideoFrame->frameId == rhs->sharedVideoFrame->frameId && lhs < rhs);
+ }
+};
+
+class Worker {
+public:
+ explicit Worker(unsigned threadNum):
+ threadPool(threadNum), running{false} {}
+ ~Worker() {
+ stop();
+ }
+ void runThreads() {
+ running = true;
+ for (std::thread& t : threadPool) {
+ t = std::thread(&Worker::threadFunc, this);
+ }
+ }
+ void push(std::shared_ptr<Task> task) {
+ tasksMutex.lock();
+ tasks.insert(task);
+ tasksMutex.unlock();
+ tasksCondVar.notify_one();
+ }
+ void threadFunc() {
+ while (running) {
+ std::unique_lock<std::mutex> lk(tasksMutex);
+ while (running && tasks.empty()) {
+ tasksCondVar.wait(lk);
+ }
+ try {
+ auto it = std::find_if(tasks.begin(), tasks.end(), [](const std::shared_ptr<Task>& task){return task->isReady();});
+ if (tasks.end() != it) {
+ const std::shared_ptr<Task> task = std::move(*it);
+ tasks.erase(it);
+ lk.unlock();
+ task->process();
+ }
+ } catch (...) {
+ std::lock_guard<std::mutex> lock{exceptionMutex};
+ if (nullptr == currentException) {
+ currentException = std::current_exception();
+ stop();
+ }
+ }
+ }
+ }
+ void stop() {
+ running = false;
+ tasksCondVar.notify_all();
+ }
+ void join() {
+ for (auto& t : threadPool) {
+ t.join();
+ }
+ if (nullptr != currentException) {
+ std::rethrow_exception(currentException);
+ }
+ }
+
+private:
+ std::condition_variable tasksCondVar;
+ std::set<std::shared_ptr<Task>, HigherPriority> tasks;
+ std::mutex tasksMutex;
+ std::vector<std::thread> threadPool;
+ std::atomic<bool> running;
+ std::exception_ptr currentException;
+ std::mutex exceptionMutex;
+};
+
+void tryPush(const std::weak_ptr<Worker>& worker, std::shared_ptr<Task>&& task) {
+ try {
+ std::shared_ptr<Worker>(worker)->push(task);
+ } catch (const std::bad_weak_ptr&) {}
+}
+
+template <class C> class ConcurrentContainer {
+public:
+ C container;
+ mutable std::mutex mutex;
+
+ bool lockedEmpty() const noexcept {
+ std::lock_guard<std::mutex> lock{mutex};
+ return container.empty();
+ }
+ typename C::size_type lockedSize() const noexcept {
+ std::lock_guard<std::mutex> lock{mutex};
+ return container.size();
+ }
+ void lockedPushBack(const typename C::value_type& value) {
+ std::lock_guard<std::mutex> lock{mutex};
+ container.push_back(value);
+ }
+ bool lockedTryPop(typename C::value_type& value) {
+ mutex.lock();
+ if (!container.empty()) {
+ value = container.back();
+ container.pop_back();
+ mutex.unlock();
+ return true;
+ } else {
+ mutex.unlock();
+ return false;
+ }
+ }
+
+ operator C() const {
+ std::lock_guard<std::mutex> lock{mutex};
+ return container;
+ }
+};