plugin  0.1.0
ocv_common.hpp
Go to the documentation of this file.
1 // Copyright (C) 2018-2024 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
10 #pragma once
11 
12 #include <opencv2/opencv.hpp>
13 #include <openvino/openvino.hpp>
14 
15 #include "utils/common.hpp"
16 #include "utils/shared_tensor_allocator.hpp"
17 
21 template <typename T>
22 const T getMatValue(const cv::Mat& mat, size_t h, size_t w, size_t c) {
23  switch (mat.type()) {
24  case CV_8UC1: return (T)mat.at<uchar>(h, w);
25  case CV_8UC3: return (T)mat.at<cv::Vec3b>(h, w)[c];
26  case CV_32FC1: return (T)mat.at<float>(h, w);
27  case CV_32FC3: return (T)mat.at<cv::Vec3f>(h, w)[c];
28  }
29  throw std::runtime_error("cv::Mat type is not recognized");
30 };
31 
38 static UNUSED void matToTensor(const cv::Mat& mat, const ov::Tensor& tensor, int batchIndex = 0) {
39  ov::Shape tensorShape = tensor.get_shape();
40  static const ov::Layout layout("NCHW");
41  const size_t width = tensorShape[ov::layout::width_idx(layout)];
42  const size_t height = tensorShape[ov::layout::height_idx(layout)];
43  const size_t channels = tensorShape[ov::layout::channels_idx(layout)];
44  if (static_cast<size_t>(mat.channels()) != channels) {
45  throw std::runtime_error("The number of channels for model input and image must match");
46  }
47  if (channels != 1 && channels != 3) {
48  throw std::runtime_error("Unsupported number of channels");
49  }
50  int batchOffset = batchIndex * width * height * channels;
51 
52  cv::Mat resizedMat;
53  if (static_cast<int>(width) != mat.size().width || static_cast<int>(height) != mat.size().height) {
54  cv::resize(mat, resizedMat, cv::Size(width, height));
55  } else {
56  resizedMat = mat;
57  }
58 
59  if (tensor.get_element_type() == ov::element::f32) {
60  float_t* tensorData = tensor.data<float_t>();
61  for (size_t c = 0; c < channels; c++)
62  for (size_t h = 0; h < height; h++)
63  for (size_t w = 0; w < width; w++)
64  tensorData[batchOffset + c * width * height + h * width + w] =
65  getMatValue<float_t>(resizedMat, h, w, c);
66  } else {
67  uint8_t* tensorData = tensor.data<uint8_t>();
68  if (resizedMat.depth() == CV_32F) {
69  throw std::runtime_error("Conversion of cv::Mat from float_t to uint8_t is forbidden");
70  }
71  for (size_t c = 0; c < channels; c++)
72  for (size_t h = 0; h < height; h++)
73  for (size_t w = 0; w < width; w++)
74  tensorData[batchOffset + c * width * height + h * width + w] =
75  getMatValue<uint8_t>(resizedMat, h, w, c);
76  }
77 }
78 
79 static UNUSED ov::Tensor wrapMat2Tensor(const cv::Mat& mat) {
80  auto matType = mat.type() & CV_MAT_DEPTH_MASK;
81  if (matType != CV_8U && matType != CV_32F) {
82  throw std::runtime_error("Unsupported mat type for wrapping");
83  }
84  bool isMatFloat = matType == CV_32F;
85 
86  const size_t channels = mat.channels();
87  const size_t height = mat.rows;
88  const size_t width = mat.cols;
89 
90  const size_t strideH = mat.step.buf[0];
91  const size_t strideW = mat.step.buf[1];
92 
93  const bool isDense = !isMatFloat ? (strideW == channels && strideH == channels * width) :
94  (strideW == channels * sizeof(float) && strideH == channels * width * sizeof(float));
95  if (!isDense) {
96  throw std::runtime_error("Doesn't support conversion from not dense cv::Mat");
97  }
98  auto precision = isMatFloat ? ov::element::f32 : ov::element::u8;
99  return ov::Tensor(precision, ov::Shape{ 1, height, width, channels }, SharedMatAllocator{mat});
100 }
101 
102 static inline void resize2tensor(const cv::Mat& mat, const ov::Tensor& tensor) {
103  static const ov::Layout layout{"NHWC"};
104  const ov::Shape& shape = tensor.get_shape();
105  cv::Size size{int(shape[ov::layout::width_idx(layout)]), int(shape[ov::layout::height_idx(layout)])};
106  assert(tensor.get_element_type() == ov::element::u8);
107  assert(shape.size() == 4);
108  assert(shape[ov::layout::batch_idx(layout)] == 1);
109  assert(shape[ov::layout::channels_idx(layout)] == 3);
110  cv::resize(mat, cv::Mat{size, CV_8UC3, tensor.data()}, size);
111 }
112 
114  using DimType = size_t;
115  using IndexType = size_t;
116  using ConditionChecker = std::function<bool(IndexType, const ov::PartialShape&)>;
117 
118  template<class Cond>
119  constexpr IntervalCondition(IndexType i1, IndexType i2, Cond c) :
120  impl([=](IndexType i0, const ov::PartialShape& shape) {
121  return c(shape[i0].get_max_length(), shape[i1].get_max_length()) && c(shape[i0].get_max_length(), shape[i2].get_max_length());})
122  {}
123  bool operator() (IndexType i0, const ov::PartialShape& shape) const { return impl(i0, shape); }
124 private:
125  ConditionChecker impl;
126 };
127 
128 template <template<class> class Cond, class ...Args>
129 IntervalCondition makeCond(Args&&...args) {
130  return IntervalCondition(std::forward<Args>(args)..., Cond<IntervalCondition::DimType>{});
131 }
132 using LayoutCondition = std::tuple<size_t/*dim index*/, IntervalCondition, std::string>;
133 
134 static inline std::tuple<bool, ov::Layout> makeGuesLayoutFrom4DShape(const ov::PartialShape& shape) {
135  // at the moment we make assumption about NCHW & NHCW only
136  // if hypothetical C value is less than hypothetical H and W - then
137  // out assumption is correct and we pick a corresponding layout
138  static const std::array<LayoutCondition, 2> hypothesisMatrix {{
139  {1, makeCond<std::less_equal>(2, 3), "NCHW"},
140  {3, makeCond<std::less_equal>(1, 2), "NHWC"}
141  }};
142  for (const auto &h : hypothesisMatrix) {
143 
144  auto channel_index = std::get<0>(h);
145  const auto &cond = std::get<1>(h);
146  if (cond(channel_index, shape)) {
147  return std::make_tuple(true, ov::Layout{std::get<2>(h)});
148  }
149  }
150  return {false, ov::Layout{}};
151 }
152 
153 static inline ov::Layout getLayoutFromShape(const ov::PartialShape& shape) {
154  if (shape.size() == 2) {
155  return "NC";
156  }
157  if (shape.size() == 3) {
158  if (shape[0] == 1) {
159  return "NHW";
160  }
161  if (shape[2] == 1) {
162  return "HWN";
163  }
164  throw std::runtime_error("Can't guess layout for " + shape.to_string());
165  }
166  if (shape.size() == 4) {
167  if (ov::Interval{1, 4}.contains(shape[1].get_interval())) {
168  return "NCHW";
169  }
170  if (ov::Interval{1, 4}.contains(shape[3].get_interval())) {
171  return "NHWC";
172  }
173  if (shape[1] == shape[2]) {
174  return "NHWC";
175  }
176  if (shape[2] == shape[3]) {
177  return "NCHW";
178  }
179  bool guesResult = false;
180  ov::Layout guessedLayout;
181  std::tie(guesResult, guessedLayout) = makeGuesLayoutFrom4DShape(shape);
182  if (guesResult) {
183  return guessedLayout;
184  }
185  }
186  throw std::runtime_error("Usupported " + std::to_string(shape.size()) + "D shape");
187 }
188 
200 inline void putHighlightedText(const cv::Mat& frame,
201  const std::string& message,
202  cv::Point position,
203  int fontFace,
204  double fontScale,
205  cv::Scalar color,
206  int thickness) {
207  cv::putText(frame, message, position, fontFace, fontScale, cv::Scalar(255, 255, 255), thickness + 1);
208  cv::putText(frame, message, position, fontFace, fontScale, color, thickness);
209 }
210 
211 // TODO: replace with Size::empty() after OpenCV3 is dropped
212 static inline bool isSizeEmpty(const cv::Size& size) {
213  return size.width <= 0 || size.height <= 0;
214 }
215 
216 // TODO: replace with Rect::empty() after OpenCV3 is dropped
217 static inline bool isRectEmpty(const cv::Rect& rect) {
218  return rect.width <= 0 || rect.height <= 0;
219 }
220 
222 public:
223  OutputTransform() : doResize(false), scaleFactor(1) {}
224 
225  OutputTransform(cv::Size inputSize, cv::Size outputResolution) :
226  doResize(true), scaleFactor(1), inputSize(inputSize), outputResolution(outputResolution) {}
227 
228  cv::Size computeResolution() {
229  float inputWidth = static_cast<float>(inputSize.width);
230  float inputHeight = static_cast<float>(inputSize.height);
231  scaleFactor = std::min(outputResolution.height / inputHeight, outputResolution.width / inputWidth);
232  newResolution = cv::Size{static_cast<int>(inputWidth * scaleFactor), static_cast<int>(inputHeight * scaleFactor)};
233  return newResolution;
234  }
235 
236  void resize(cv::Mat& image) {
237  if (!doResize) { return; }
238  cv::Size currSize = image.size();
239  if (currSize != inputSize) {
240  inputSize = currSize;
241  computeResolution();
242  }
243  if (scaleFactor == 1) { return; }
244  cv::resize(image, image, newResolution);
245  }
246 
247  template<typename T>
248  void scaleCoord(T& coord) {
249  if (!doResize || scaleFactor == 1) { return; }
250  coord.x = std::floor(coord.x * scaleFactor);
251  coord.y = std::floor(coord.y * scaleFactor);
252  }
253 
254  template<typename T>
255  void scaleRect(T& rect) {
256  if (!doResize || scaleFactor == 1) { return; }
257  scaleCoord(rect);
258  rect.width = std::floor(rect.width * scaleFactor);
259  rect.height = std::floor(rect.height * scaleFactor);
260  }
261 
262  bool doResize;
263 
264 private:
265  float scaleFactor;
266  cv::Size inputSize;
267  cv::Size outputResolution;
268  cv::Size newResolution;
269 };
270 
272 public:
273  InputTransform() : reverseInputChannels(false), isTrivial(true) {}
274 
275  InputTransform(bool reverseInputChannels, const std::string& meanValues, const std::string& scaleValues) :
276  reverseInputChannels(reverseInputChannels),
277  isTrivial(!reverseInputChannels && meanValues.empty() && scaleValues.empty()),
278  means(meanValues.empty() ? cv::Scalar(0.0, 0.0, 0.0) : string2Vec(meanValues)),
279  stdScales(scaleValues.empty() ? cv::Scalar(1.0, 1.0, 1.0) : string2Vec(scaleValues)) {
280  }
281 
282  cv::Scalar string2Vec(const std::string& string) {
283  const auto& strValues = split(string, ' ');
284  std::vector<float> values;
285  try {
286  for (auto& str : strValues)
287  values.push_back(std::stof(str));
288  }
289  catch (const std::invalid_argument&) {
290  throw std::runtime_error("Invalid parameter --mean_values or --scale_values is provided.");
291  }
292  if (values.size() != 3) {
293  throw std::runtime_error("InputTransform expects 3 values per channel, but get \"" + string + "\".");
294  }
295  return cv::Scalar(values[0], values[1], values[2]);
296  }
297 
298  void setPrecision(ov::preprocess::PrePostProcessor& ppp, const std::string& tensorName) {
299  const auto precision = isTrivial ? ov::element::u8 : ov::element::f32;
300  ppp.input(tensorName).tensor().
301  set_element_type(precision);
302  }
303 
304  cv::Mat operator()(const cv::Mat& inputs) {
305  if (isTrivial) { return inputs; }
306  cv::Mat result;
307  inputs.convertTo(result, CV_32F);
308  if (reverseInputChannels) {
309  cv::cvtColor(result, result, cv::COLOR_BGR2RGB);
310  }
311  // TODO: merge the two following lines after OpenCV3 is droppped
312  result -= means;
313  result /= cv::Mat{stdScales};
314  return result;
315  }
316 
317 private:
318  bool reverseInputChannels;
319  bool isTrivial;
320  cv::Scalar means;
321  cv::Scalar stdScales;
322 };
323 
325  cv::VideoWriter writer;
326  unsigned nwritten;
327 public:
328  const std::string filenames;
329  const double fps;
330  const unsigned lim;
331 
332  LazyVideoWriter(const std::string& filenames, double fps, unsigned lim) :
333  nwritten{1}, filenames{filenames}, fps{fps}, lim{lim} {}
334  void write(const cv::Mat& im) {
335  if (writer.isOpened() && (nwritten < lim || 0 == lim)) {
336  writer.write(im);
337  ++nwritten;
338  return;
339  }
340  if (!writer.isOpened() && !filenames.empty()) {
341  if (!writer.open(filenames, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, im.size())) {
342  throw std::runtime_error("Can't open video writer");
343  }
344  writer.write(im);
345  }
346  }
347 };
Definition: ocv_common.hpp:271
Definition: ocv_common.hpp:324
Definition: ocv_common.hpp:221
a header file with common samples functionality
const T getMatValue(const cv::Mat &mat, size_t h, size_t w, size_t c)
Get cv::Mat value in the correct format.
Definition: ocv_common.hpp:22
void putHighlightedText(const cv::Mat &frame, const std::string &message, cv::Point position, int fontFace, double fontScale, cv::Scalar color, int thickness)
Puts text message on the frame, highlights the text with a white border to make it distinguishable fr...
Definition: ocv_common.hpp:200
Definition: ocv_common.hpp:113
Definition: shared_tensor_allocator.hpp:21