JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
Utils.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2020 by Laurent Itti, the University of Southern
4// California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5//
6// This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7// redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8// Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10// License for more details. You should have received a copy of the GNU General Public License along with this program;
11// if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12//
13// Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14// Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16/*! \file */
17
18#include <jevois/DNN/Utils.H>
19#include <jevois/Util/Utils.H>
20#include <jevois/Debug/Log.H>
21#include <fstream>
22#include <cstring> // for std::memcpy()
23
24// ##############################################################################################################
25std::map<int, std::string> jevois::dnn::readLabelsFile(std::string const & fname)
26{
27 std::ifstream ifs(fname);
28 if (ifs.is_open() == false) LFATAL("Failed to open file " << fname);
29
30 size_t linenum = 1; std::map<int, std::string> ret; int id = 0;
31 for (std::string line; std::getline(ifs, line); ++linenum)
32 {
33 size_t idx1 = line.find_first_not_of(" \t"); if (idx1 == line.npos) continue;
34 size_t idx2 = line.find_last_not_of(" \t\r\n"); if (idx2 == line.npos) continue;
35 if (line[idx1] == '#') continue;
36
37 try { id = std::stoi(line, &idx1); idx1 = line.find_first_not_of("0123456789 \t,:", idx1); } catch (...) { }
38
39 std::string classname;
40 if (idx1 >= idx2)
41 {
42 LERROR(fname << ':' << linenum << ": empty class name -- REPLACING BY 'unspecified'");
43 classname = "unspecified";
44 }
45 else classname = line.substr(idx1, idx2 + 1 - idx1);
46
47 // Possibly replace two double quotes by one:
48 jevois::replaceStringAll(classname, "\"\"", "\"");
49
50 // Possibly remove enclosing double quotes:
51 size_t len = classname.length();
52 if (len > 1 && classname[0] == '"' && classname[len-1] == '"') classname = classname.substr(1, len-2);
53
54 ret[id] = classname;
55
56 // Increment id in case no ID number is given in the file:
57 ++id;
58 }
59
60 ifs.close();
61
62 LINFO("Loaded " << ret.size() << " class names from " << fname);
63
64 return ret;
65}
66
67// ##############################################################################################################
68std::string jevois::dnn::getLabel(std::map<int, std::string> const & labels, int id, bool namedonly)
69{
70 auto itr = labels.find(id);
71 if (itr == labels.end())
72 {
73 if (namedonly) return std::string();
74 else return std::to_string(id);
75 }
76 return itr->second;
77}
78
79// ##############################################################################################################
80int jevois::dnn::stringToRGBA(std::string const & label, unsigned char alpha)
81{
82 int col = 0x80808080;
83 for (char const c : label) col = c + ((col << 5) - col);
84 col = (col & 0xffffff) | (alpha << 24);
85 return col;
86}
87
88// ##############################################################################################################
89void jevois::dnn::topK(float const * pfProb, float * pfMaxProb, uint32_t * pMaxClass, uint32_t outputCount,
90 uint32_t topNum)
91{
92 memset(pfMaxProb, 0xfe, sizeof(float) * topNum);
93 memset(pMaxClass, 0xff, sizeof(float) * topNum);
94
95 for (uint32_t j = 0; j < topNum; ++j)
96 {
97 for (uint32_t i = 0; i < outputCount; ++i)
98 {
99 uint32_t k;
100 for (k = 0; k < topNum; ++k) if (i == pMaxClass[k]) break;
101 if (k != topNum) continue;
102
103 if (pfProb[i] > pfMaxProb[j]) { pfMaxProb[j] = pfProb[i]; pMaxClass[j] = i; }
104 }
105 }
106}
107
108// ##############################################################################################################
109std::string jevois::dnn::shapestr(cv::Mat const & m)
110{
111 cv::MatSize const & ms = m.size; int const nd = ms.dims();
112 std::string ret = std::to_string(nd) + "D ";
113 for (int i = 0; i < nd; ++i) ret += std::to_string(ms[i]) + (i < nd-1 ? "x" : "");
114 ret += ' ' + jevois::cvtypestr(m.type());
115 return ret;
116}
117
118// ##############################################################################################################
119std::string jevois::dnn::shapestr(std::vector<size_t> dims, int typ)
120{
121 int const nd = int(dims.size());
122 std::string ret = std::to_string(nd) + "D ";
123 for (int i = 0; i < nd; ++i) ret += std::to_string(dims[i]) + (i < nd-1 ? "x" : "");
124 ret += ' ' + jevois::cvtypestr(typ);
125 return ret;
126}
127
128// ##############################################################################################################
129std::string jevois::dnn::shapestr(std::vector<int> dims, int typ)
130{
131 int const nd = int(dims.size());
132 std::string ret = std::to_string(nd) + "D ";
133 for (int i = 0; i < nd; ++i) ret += std::to_string(dims[i]) + (i < nd-1 ? "x" : "");
134 ret += ' ' + jevois::cvtypestr(typ);
135 return ret;
136}
137
138// ##############################################################################################################
139std::string jevois::dnn::shapestr(TfLiteTensor const * t)
140{
141
142 TfLiteIntArray const & dims = *t->dims;
143 std::string ret = std::to_string(dims.size) + "D ";
144 for (int i = 0; i < dims.size; ++i) ret += std::to_string(dims.data[i]) + (i < dims.size-1 ? "x" : "");
145
146 // Do not use TfLiteTypeGetName() as it returns different names...
147 switch (t->type)
148 {
149 case kTfLiteNoType: ret += " NoType"; break;
150 case kTfLiteFloat32: ret += " 32F"; break;
151 case kTfLiteInt32: ret += " 32S"; break;
152 case kTfLiteUInt8: ret += " 8U"; break;
153 case kTfLiteInt64: ret += " 64S"; break;
154 case kTfLiteString: ret += " String"; break;
155 case kTfLiteBool: ret += " 8B"; break;
156 case kTfLiteInt16: ret += " 16S"; break;
157 case kTfLiteComplex64: ret += " 64C"; break;
158 case kTfLiteInt8: ret += " 8I"; break;
159 case kTfLiteFloat16: ret += " 16F"; break;
160 case kTfLiteFloat64: ret += " 64F"; break;
161 case kTfLiteComplex128: ret += " 128C"; break;
162 default: ret += " UnknownType"; break;
163 }
164 return ret;
165}
166
167// ##############################################################################################################
168std::string jevois::dnn::shapestr(vsi_nn_tensor_attr_t const & attr)
169{
170 std::string ret = std::to_string(attr.dim_num) + "D ";
171 for (uint32_t i = 0; i < attr.dim_num; ++i)
172 ret += std::to_string(attr.size[attr.dim_num-1-i]) + (i < attr.dim_num-1 ? "x" : "");
173
174 // Value type:
175 switch (attr.dtype.vx_type)
176 {
177 case VSI_NN_TYPE_UINT8: ret += " 8U"; break;
178 case VSI_NN_TYPE_INT8: ret += " 8S"; break;
179 case VSI_NN_TYPE_BOOL8: ret += " 8B"; break;
180 case VSI_NN_TYPE_UINT16: ret += " 16U"; break;
181 case VSI_NN_TYPE_INT16: ret += " 16S"; break;
182 case VSI_NN_TYPE_FLOAT16: ret += " 16F"; break;
183 case VSI_NN_TYPE_BFLOAT16: ret += " 16B"; break;
184 case VSI_NN_TYPE_UINT32: ret += " 32U"; break;
185 case VSI_NN_TYPE_INT32: ret += " 32S"; break;
186 case VSI_NN_TYPE_FLOAT32: ret += " 32F"; break;
187 case VSI_NN_TYPE_UINT64: ret += " 64U"; break;
188 case VSI_NN_TYPE_INT64: ret += " 64S"; break;
189 case VSI_NN_TYPE_FLOAT64: ret += " 64F"; break;
190 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(attr.dtype.vx_type));
191 }
192
193 return ret;
194}
195
196#ifdef JEVOIS_PRO
197// ##############################################################################################################
198std::string jevois::dnn::shapestr(Ort::ConstTensorTypeAndShapeInfo const & ti)
199{
200 std::ostringstream os;
201 std::vector<int64_t> input_node_dims = ti.GetShape();
202 os << input_node_dims.size() << "D ";
203 for (int64_t d : input_node_dims) os << d << 'x';
204 os.seekp(-1, os.cur); // will overwrite last 'x'
205
206 switch (ti.GetElementType())
207 {
208 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED: os << " UNDEFINED"; break;
209 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: os << " 32F"; break;
210 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: os << " 8U"; break;
211 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: os << " 8S"; break;
212 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: os << " 16U"; break;
213 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: os << " 16S"; break;
214 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: os << " 32S"; break;
215 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: os << " 64S"; break;
216 case ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING: os << " STR"; break;
217 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: os << " BOOL"; break;
218 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: os << " 16F"; break;
219 case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: os << " 64F"; break;
220 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: os << " 32U"; break;
221 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: os << " 64U"; break;
222 case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64: os << " 64CPLX"; break;
223 case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128: os << " 128CPLX"; break;
224 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16: os << " 16B"; break;
225 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(ti.GetElementType()));
226 }
227
228 return os.str();
229}
230
231// ##############################################################################################################
232vsi_nn_type_e jevois::dnn::onnx2vsi(ONNXTensorElementDataType t)
233{
234 switch (t)
235 {
236 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED: return VSI_NN_TYPE_NONE;
237 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return VSI_NN_TYPE_FLOAT32;
238 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: return VSI_NN_TYPE_UINT8;
239 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: return VSI_NN_TYPE_INT8;
240 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: return VSI_NN_TYPE_UINT16;
241 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: return VSI_NN_TYPE_INT16;
242 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return VSI_NN_TYPE_INT32;
243 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return VSI_NN_TYPE_INT64;
244 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: return VSI_NN_TYPE_BOOL8;
245 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: return VSI_NN_TYPE_FLOAT16;
246 case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: return VSI_NN_TYPE_FLOAT64;
247 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: return VSI_NN_TYPE_UINT32;
248 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: return VSI_NN_TYPE_UINT64;
249 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16: return VSI_NN_TYPE_BFLOAT16;
250 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING: // unsupported by VSI
251 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64:
252 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128:
253 default: throw std::range_error("onnx2vsi: Unsupported tensor type " + std::to_string(t));
254 }
255}
256
257// ##############################################################################################################
258vsi_nn_tensor_attr_t jevois::dnn::tensorattr(Ort::ConstTensorTypeAndShapeInfo const & ti)
259{
260 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
261 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
262 attr.dtype.vx_type = jevois::dnn::onnx2vsi(ti.GetElementType());
263
264 std::vector<int64_t> const dims = ti.GetShape();
265 size_t const ds = dims.size();
266 attr.dim_num = ds;
267 for (size_t i = 0; i < ds; ++i) attr.size[ds - 1 - i] = dims[i];
268
269 // FIXME: quantization not yet supported
270 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
271
272 return attr;
273}
274
275// ##############################################################################################################
276std::string jevois::dnn::shapestr(hailo_vstream_info_t const & vi)
277{
278 // FIXME: should optimize but beware that vi.shape may have different interpretations depending on NCHW vs NWCH etc
279 return shapestr(tensorattr(vi));
280
281 /*
282 std::string ret = "3D ";
283 switch (+ std::to_string(vi.shape[0]) + 'x' +
284 std::to_string(vi.shape[1]) + 'x' + std::to_string(vi.shape[2]);
285
286 switch (vi.format.type)
287 {
288 case HAILO_FORMAT_TYPE_AUTO: ret += " AUTO"; break;
289 case HAILO_FORMAT_TYPE_UINT8: ret += " 8U"; break;
290 case HAILO_FORMAT_TYPE_UINT16: ret += " 16U"; break;
291 case HAILO_FORMAT_TYPE_FLOAT32: ret += " 32F"; break;
292 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(vi.format));
293 }
294
295 return ret;
296 */
297}
298#endif
299
300// ##############################################################################################################
301std::vector<size_t> jevois::dnn::strshape(std::string const & str)
302{
303 std::vector<size_t> ret;
304 auto tok = jevois::split(str, "x");
305 for (std::string const & t : tok) ret.emplace_back(std::stoi(t));
306 return ret;
307}
308
309// ##############################################################################################################
310int jevois::dnn::tf2cv(TfLiteType t)
311{
312 switch (t)
313 {
314 case kTfLiteFloat32: return CV_32F;
315 case kTfLiteInt32: return CV_32S;
316 case kTfLiteUInt8: return CV_8U;
317 case kTfLiteInt16: return CV_16S;
318 case kTfLiteInt8: return CV_8S;
319 case kTfLiteFloat16: return CV_16F;
320 case kTfLiteFloat64: return CV_64F;
321 //case kTfLiteComplex128:
322 //case kTfLiteComplex64:
323 //case kTfLiteBool:
324 //case kTfLiteString:
325 //case kTfLiteInt64:
326 //case kTfLiteNoType:
327 default: throw std::range_error(std::string("tf2cv: Unsupported type ") + TfLiteTypeGetName(t));
328 }
329}
330
331// ##############################################################################################################
332int jevois::dnn::vsi2cv(vsi_nn_type_e t)
333{
334 switch (t)
335 {
336 case VSI_NN_TYPE_UINT8: return CV_8U;
337 case VSI_NN_TYPE_INT8: return CV_8S;
338 case VSI_NN_TYPE_BOOL8: return CV_8U;
339 case VSI_NN_TYPE_UINT16: return CV_16U;
340 case VSI_NN_TYPE_INT16: return CV_16S;
341 case VSI_NN_TYPE_FLOAT16: return CV_16F;
342 //case VSI_NN_TYPE_BFLOAT16: return CV_16F; // check
343 //case VSI_NN_TYPE_UINT32: return CV_32U; // unsupported by opencv
344 case VSI_NN_TYPE_INT32: return CV_32S;
345 case VSI_NN_TYPE_FLOAT32: return CV_32F;
346 //case VSI_NN_TYPE_UINT64: return CV_64U; // unsupported by opencv
347 //case VSI_NN_TYPE_INT64: return CV_64S; // unsupported by opencv
348 case VSI_NN_TYPE_FLOAT64: return CV_64F;
349 default: throw std::range_error("vsi2cv: Unsupported tensor type " + std::to_string(t));
350 }
351}
352
353// ##############################################################################################################
354vsi_nn_type_e jevois::dnn::tf2vsi(TfLiteType t)
355{
356 switch (t)
357 {
358 case kTfLiteFloat32: return VSI_NN_TYPE_FLOAT32;
359 case kTfLiteInt32: return VSI_NN_TYPE_INT32;
360 case kTfLiteUInt8: return VSI_NN_TYPE_UINT8;
361 case kTfLiteInt16: return VSI_NN_TYPE_INT16;
362 case kTfLiteInt8: return VSI_NN_TYPE_INT8;
363 case kTfLiteFloat16: return VSI_NN_TYPE_FLOAT16;
364 case kTfLiteFloat64: return VSI_NN_TYPE_FLOAT64;
365 case kTfLiteInt64: return VSI_NN_TYPE_INT64;
366 case kTfLiteBool: return VSI_NN_TYPE_BOOL8; // fixme: need to check
367 case kTfLiteNoType: return VSI_NN_TYPE_NONE;
368 //case kTfLiteComplex128:
369 //case kTfLiteComplex64:
370 //case kTfLiteString:
371 default: throw std::range_error(std::string("tf2vsi: Unsupported type ") + TfLiteTypeGetName(t));
372 }
373}
374
375// ##############################################################################################################
376#ifdef JEVOIS_PRO
377vsi_nn_type_e jevois::dnn::hailo2vsi(hailo_format_type_t t)
378{
379 switch (t)
380 {
381 case HAILO_FORMAT_TYPE_AUTO: return VSI_NN_TYPE_NONE; break; // or throw?
382 case HAILO_FORMAT_TYPE_UINT8: return VSI_NN_TYPE_UINT8; break;
383 case HAILO_FORMAT_TYPE_UINT16: return VSI_NN_TYPE_UINT16; break;
384 case HAILO_FORMAT_TYPE_FLOAT32: return VSI_NN_TYPE_FLOAT32; break;
385 default: throw std::range_error("hailo2vsi: Unsupported tensor type " + std::to_string(t));
386 }
387}
388#endif
389
390// ##############################################################################################################
391void jevois::dnn::clamp(cv::Rect & r, int width, int height)
392{
393 int tx = std::min(width - 1, std::max(0, r.x));
394 int ty = std::min(height - 1, std::max(0, r.y));
395 int bx = std::min(width - 1, std::max(0, r.x + r.width));
396 int by = std::min(height - 1, std::max(0, r.y + r.height));
397 r.x = tx; r.y = ty; r.width = bx - tx; r.height = by - ty;
398}
399
400// ##############################################################################################################
401void jevois::dnn::clamp(cv::Rect2f & r, float width, float height)
402{
403 float tx = std::min(width - 1.0F, std::max(0.0F, r.x));
404 float ty = std::min(height - 1.0F, std::max(0.0F, r.y));
405 float bx = std::min(width - 1.0F, std::max(0.0F, r.x + r.width));
406 float by = std::min(height - 1.0F, std::max(0.0F, r.y + r.height));
407 r.x = tx; r.y = ty; r.width = bx - tx; r.height = by - ty;
408}
409
410// ##############################################################################################################
411std::vector<vsi_nn_tensor_attr_t> jevois::dnn::parseTensorSpecs(std::string const & specs)
412{
413 char const * const specdef = "[NCHW:|NHWC:|NA:|AUTO:]Type:[NxCxHxW|NxHxWxC|...][:QNT[:fl|:scale:zero]]";
414 std::vector<std::string> spectok = jevois::split(specs, ",\\s*");
415 std::vector<vsi_nn_tensor_attr_t> ret;
416
417 for (std::string const & spec : spectok)
418 {
419 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
420
421 // NCHW:Type:NxCxHxW:QNT:scale:mean
422 std::vector<std::string> tok = jevois::split(spec, ":");
423 if (tok.size() < 2) throw std::runtime_error("parseTensorSpecs: Malformed tensor spec ["+spec+"] not "+specdef);
424
425 // Decode optional shape:
426 size_t n = 0; // next tok to parse
427 if (tok[0] == "NCHW") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW; } // planar RGB
428 else if (tok[0] == "NHWC") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC; } // packed RGB
429 else if (tok[0] == "NA") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NA; }
430 else if (tok[0] == "AUTO") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO; }
431 else attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO; // use AUTO if it was not given
432
433 // We need at least type and dims:
434 if (tok.size() < n+2) throw std::runtime_error("parseTensorSpecs: Malformed tensor spec ["+spec+"] not "+specdef);
435
436 // Decode type and convert to vsi:
437 if (tok[n] == "8U") attr.dtype.vx_type = VSI_NN_TYPE_UINT8;
438 else if (tok[n] == "8S") attr.dtype.vx_type = VSI_NN_TYPE_INT8;
439 else if (tok[n] == "8B") attr.dtype.vx_type = VSI_NN_TYPE_BOOL8;
440 else if (tok[n] == "16U") attr.dtype.vx_type = VSI_NN_TYPE_UINT16;
441 else if (tok[n] == "16S") attr.dtype.vx_type = VSI_NN_TYPE_INT16;
442 else if (tok[n] == "16F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT16;
443 else if (tok[n] == "16B") attr.dtype.vx_type = VSI_NN_TYPE_BFLOAT16;
444 else if (tok[n] == "32U") attr.dtype.vx_type = VSI_NN_TYPE_UINT32;
445 else if (tok[n] == "32S") attr.dtype.vx_type = VSI_NN_TYPE_INT32;
446 else if (tok[n] == "32F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT32;
447 else if (tok[n] == "64U") attr.dtype.vx_type = VSI_NN_TYPE_UINT64;
448 else if (tok[n] == "64S") attr.dtype.vx_type = VSI_NN_TYPE_INT64;
449 else if (tok[n] == "64F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT64;
450 else throw std::range_error("parseTensorSpecs: Invalid tensor type [" + tok[n] + "] in " + spec);
451 ++n; // next token
452
453 // Decode the dims:
454 std::vector<size_t> dims = jevois::dnn::strshape(tok[n]);
455 attr.dim_num = dims.size();
456 for (size_t i = 0; i < attr.dim_num; ++i) attr.size[attr.dim_num - 1 - i] = dims[i];
457 ++n; // next token
458
459 // Decode optional quantization type and its possible extra parameters:
460 if (n == tok.size() || tok[n] == "NONE")
461 {
462 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
463 }
464 else if (tok[n] == "DFP")
465 {
466 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_DFP;
467 if (tok.size() != n+2)
468 throw std::range_error("parseTensorSpecs: In "+spec+", DFP quantization needs :fl (" + specdef + ')');
469 attr.dtype.fl = std::stoi(tok[n+1]);
470 }
471
472 else if (tok[n] == "AA" || tok[n] == "AS") // affine asymmetric and symmetric same, see ovxlib/vsi_nn_tensor.h
473 {
474 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
475 if (tok.size() != n+3)
476 throw std::range_error("parseTensorSpecs: In "+spec+", AA/AS quantization needs :scale:zero ("+specdef+')');
477 attr.dtype.scale = std::stof(tok[n+1]);
478 attr.dtype.zero_point = std::stoi(tok[n+2]);
479 }
480 else if (tok[n] == "APS")
481 {
482 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC;
483 throw std::range_error("parseTensorSpecs: In " + spec + ", AFFINE_PERCHANNEL_SYMMETRIC quant not yet supported");
484 }
485 else throw std::range_error("parseTensorSpecs: Invalid quantization type in " + spec);
486
487 // Done with this tensor:
488 ret.emplace_back(attr);
489 }
490
491 return ret;
492}
493
494// ##############################################################################################################
495cv::Mat jevois::dnn::attrmat(vsi_nn_tensor_attr_t const & attr, void * dataptr)
496{
497 if (dataptr) return cv::Mat(jevois::dnn::attrdims(attr), jevois::dnn::vsi2cv(attr.dtype.vx_type), dataptr);
498 else return cv::Mat(jevois::dnn::attrdims(attr), jevois::dnn::vsi2cv(attr.dtype.vx_type));
499}
500
501// ##############################################################################################################
502std::vector<int> jevois::dnn::attrdims(vsi_nn_tensor_attr_t const & attr)
503{
504 size_t const ndim = attr.dim_num;
505 std::vector<int> cvdims(ndim);
506 for (size_t i = 0; i < ndim; ++i) cvdims[ndim - 1 - i] = attr.size[i];
507 return cvdims;
508}
509
510// ##############################################################################################################
511cv::Size jevois::dnn::attrsize(vsi_nn_tensor_attr_t const & attr)
512{
513 switch (attr.dtype.fmt)
514 {
515 case VSI_NN_DIM_FMT_NHWC:
516 if (attr.dim_num < 3) throw std::range_error("attrsize: need at least 3D, got " + jevois::dnn::attrstr(attr));
517 return cv::Size(attr.size[1], attr.size[2]);
518
519 case VSI_NN_DIM_FMT_NCHW:
520 if (attr.dim_num < 2) throw std::range_error("attrsize: need at least 2D, got " + jevois::dnn::attrstr(attr));
521 return cv::Size(attr.size[0], attr.size[1]);
522
523 case VSI_NN_DIM_FMT_AUTO:
524 if (attr.dim_num < 2) return cv::Size(attr.size[0], 1);
525 if (attr.dim_num < 3) return cv::Size(attr.size[0], attr.size[1]);
526 // ok, size[] starts with either CWH (when dim index goes 0..2) or WHC, assume C<H
527 if (attr.size[0] > attr.size[2]) return cv::Size(attr.size[0], attr.size[1]); // WHCN
528 else return cv::Size(attr.size[1], attr.size[2]); // CWHN
529
530 default:
531 throw std::range_error("attrsize: cannot extract width and height, got " + jevois::dnn::attrstr(attr));
532 }
533}
534
535// ##############################################################################################################
536std::string jevois::dnn::attrstr(vsi_nn_tensor_attr_t const & attr)
537{
538 std::string ret;
539
540 // Dimension ordering, only relevant for 3D and higher:
541 if (attr.dim_num > 2)
542 switch (attr.dtype.fmt)
543 {
544 case VSI_NN_DIM_FMT_NCHW: ret = "NCHW:"; break;
545 case VSI_NN_DIM_FMT_NHWC: ret = "NHWC:"; break;
546 default: break;
547 }
548
549 // Value type:
550 switch (attr.dtype.vx_type)
551 {
552 case VSI_NN_TYPE_UINT8: ret += "8U:"; break;
553 case VSI_NN_TYPE_INT8: ret += "8S:"; break;
554 case VSI_NN_TYPE_BOOL8: ret += "8B:"; break;
555 case VSI_NN_TYPE_UINT16: ret += "16U:"; break;
556 case VSI_NN_TYPE_INT16: ret += "16S:"; break;
557 case VSI_NN_TYPE_FLOAT16: ret += "16F:"; break;
558 case VSI_NN_TYPE_BFLOAT16: ret += "16B:"; break;
559 case VSI_NN_TYPE_UINT32: ret += "32U:"; break;
560 case VSI_NN_TYPE_INT32: ret += "32S:"; break;
561 case VSI_NN_TYPE_FLOAT32: ret += "32F:"; break;
562 case VSI_NN_TYPE_UINT64: ret += "64U:"; break;
563 case VSI_NN_TYPE_INT64: ret += "64S:"; break;
564 case VSI_NN_TYPE_FLOAT64: ret += "64F:"; break;
565 default: ret += "TYPE_UNKNOWN:";
566 }
567
568 // Dims:
569 for (uint32_t i = 0; i < attr.dim_num; ++i)
570 ret += std::to_string(attr.size[attr.dim_num - 1 - i]) + ((i<attr.dim_num-1) ? 'x' : ':');
571
572 // Quantization:
573 switch (attr.dtype.qnt_type)
574 {
575 case VSI_NN_QNT_TYPE_NONE: ret += "NONE"; break;
576 case VSI_NN_QNT_TYPE_DFP: ret += "DFP:" + std::to_string(attr.dtype.fl); break;
577 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
578 ret += "AA:" + std::to_string(attr.dtype.scale) + ':' + std::to_string(attr.dtype.zero_point);
579 break;
580 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC: ret += "APS:unsupported"; break;
581 default: ret += "QUANT_UNKNOWN";
582 }
583
584 return ret;
585}
586
587// ##############################################################################################################
588vsi_nn_tensor_attr_t jevois::dnn::tensorattr(TfLiteTensor const * t)
589{
590 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
591 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
592 attr.dtype.vx_type = jevois::dnn::tf2vsi(t->type);
593
594 switch (t->quantization.type)
595 {
596 case kTfLiteNoQuantization:
597 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
598 break;
599
600 case kTfLiteAffineQuantization:
601 {
602 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
603 attr.dtype.scale = t->params.scale;
604 attr.dtype.zero_point = t->params.zero_point;
605 }
606 break;
607
608 default: LFATAL("unsupported quantization " << t->quantization.type);
609 }
610
611 TfLiteIntArray const & dims = *t->dims;
612 attr.dim_num = dims.size;
613 for (int i = 0; i < dims.size; ++i) attr.size[dims.size - 1 - i] = dims.data[i];
614
615 // Set the fmt to NCHW or NHWC if possible:
616 if (attr.dim_num == 4)
617 {
618 if (attr.size[0] > attr.size[2]) attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW; // assume H>C
619 else attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
620 }
621
622 return attr;
623}
624
625// ##############################################################################################################
626#ifdef JEVOIS_PRO
627vsi_nn_tensor_attr_t jevois::dnn::tensorattr(hailo_vstream_info_t const & vi)
628{
629 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
630
631 attr.dtype.vx_type = hailo2vsi(vi.format.type);
632
633 switch (vi.format.order)
634 {
635 case HAILO_FORMAT_ORDER_HAILO_NMS:
636 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
637 attr.dim_num = 2;
638 attr.size[0] = vi.nms_shape.number_of_classes;
639 attr.size[1] = vi.nms_shape.max_bboxes_per_class * 5; // Each box has: xmin, ymin, xmax, ymax, score
640 break;
641
642 case HAILO_FORMAT_ORDER_NHWC:
643 case HAILO_FORMAT_ORDER_FCR:
644 case HAILO_FORMAT_ORDER_F8CR:
645 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
646 attr.dim_num = 4;
647 attr.size[0] = vi.shape.features;
648 attr.size[1] = vi.shape.width;
649 attr.size[2] = vi.shape.height;
650 attr.size[3] = 1;
651 break;
652
653 case HAILO_FORMAT_ORDER_NHW:
654 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
655 attr.dim_num = 4;
656 attr.size[0] = 1;
657 attr.size[1] = vi.shape.width;
658 attr.size[2] = vi.shape.height;
659 attr.size[3] = 1;
660 break;
661
662 case HAILO_FORMAT_ORDER_NC:
663 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
664 attr.dim_num = 4;
665 attr.size[0] = 1;
666 attr.size[1] = 1;
667 attr.size[2] = vi.shape.features;
668 attr.size[3] = 1;
669 break;
670
671 case HAILO_FORMAT_ORDER_NCHW:
672 attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW;
673 attr.dim_num = 4;
674 attr.size[0] = vi.shape.features;
675 attr.size[1] = vi.shape.width;
676 attr.size[2] = vi.shape.height;
677 attr.size[3] = 1;
678 break;
679
680 default: throw std::range_error("tensorattr: Unsupported Hailo order " +std::to_string(vi.format.order));
681 }
682
683 // Hailo only supports one quantization type:
684 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
685 attr.dtype.scale = vi.quant_info.qp_scale;
686 attr.dtype.zero_point = int32_t(vi.quant_info.qp_zp);
687
688 return attr;
689}
690#endif
691
692// ##############################################################################################################
693namespace
694{
695 struct ParallelSigmoid : public cv::ParallelLoopBody
696 {
697 ParallelSigmoid(float * ptr) : p(ptr)
698 { }
699
700 virtual void operator()(cv::Range const & r) const
701 { for (int i = r.start; i != r.end; ++i) p[i] = jevois::dnn::sigmoid(p[i]); }
702
703 private:
704 float *p;
705 };
706}
707
708void jevois::dnn::sigmoid(cv::Mat & m)
709{
710 if (m.type() != CV_32F) LFATAL("Can only apply to CV_32F tensors");
711
712 cv::parallel_for_(cv::Range(0, m.total()), ParallelSigmoid((float *)m.data));
713}
714
715// ##############################################################################################################
716size_t jevois::dnn::softmax(float const * input, size_t const n, size_t const stride, float const fac, float * output,
717 bool maxonly)
718{
719 if (stride == 0) LFATAL("Cannot work with stride = 0");
720
721 float sum = 0.0F;
722 float largest = -FLT_MAX; size_t largest_idx = 0;
723 size_t const ns = n * stride;
724
725 for (size_t i = 0; i < ns; i += stride) if (input[i] > largest) { largest = input[i]; largest_idx = i; }
726
727 if (fac == 1.0F)
728 for (size_t i = 0; i < ns; i += stride)
729 {
730 float const e = jevois::dnn::fastexp(input[i] - largest);
731 sum += e;
732 output[i] = e;
733 }
734 else
735 for (size_t i = 0; i < ns; i += stride)
736 {
737 float const e = jevois::dnn::fastexp(input[i]/fac - largest/fac);
738 sum += e;
739 output[i] = e;
740 }
741
742 if (sum)
743 {
744 if (maxonly) output[largest_idx] /= sum;
745 else for (size_t i = 0; i < ns; i += stride) output[i] /= sum;
746 }
747
748 return largest_idx;
749}
750
751// ##############################################################################################################
752float jevois::dnn::softmax_dfl(float const * src, float * dst, size_t const n, size_t const stride)
753{
754 // inspired from https://github.com/trinhtuanvubk/yolo-ncnn-cpp/blob/main/yolov8/yolov8.cpp
755 size_t const ns = n * stride;
756
757 float alpha = -FLT_MAX;
758 for (size_t c = 0; c < ns; c += stride)
759 {
760 float score = src[c];
761 if (score > alpha) alpha = score;
762 }
763
764 float denominator = 0;
765 float dis_sum = 0;
766 float * dp = dst;
767
768 for (size_t i = 0; i < ns; i += stride)
769 {
770 *dp = jevois::dnn::fastexp(src[i] - alpha);
771 denominator += *dp++;
772 }
773
774 if (denominator == 0.0F) return 0.0F;
775
776 for (size_t i = 0; i < n; ++i)
777 {
778 dst[i] /= denominator;
779 dis_sum += i * dst[i];
780 }
781 return dis_sum;
782}
783
784// ##############################################################################################################
785bool jevois::dnn::attrmatch(vsi_nn_tensor_attr_t const & attr, cv::Mat const & blob)
786{
787 // Check that blob and tensor are a complete match:
788 if (blob.channels() != 1) return false;
789 if (blob.depth() != jevois::dnn::vsi2cv(attr.dtype.vx_type)) return false;
790 if (uint32_t(blob.size.dims()) != attr.dim_num) return false;
791
792 for (size_t i = 0; i < attr.dim_num; ++i)
793 if (int(attr.size[attr.dim_num - 1 - i]) != blob.size[i]) return false;
794
795 return true;
796}
797
798// ##############################################################################################################
799cv::Mat jevois::dnn::quantize(cv::Mat const & m, vsi_nn_tensor_attr_t const & attr)
800{
801 if (m.depth() != CV_32F) LFATAL("Tensor to quantize must be 32F");
802
803 // Do a sloppy match for total size only since m may still be 2D RGB packed vs 4D attr...
804 std::vector<int> adims = jevois::dnn::attrdims(attr);
805 size_t tot = 1; for (int d : adims) tot *= d;
806
807 if (tot != m.total() * m.channels())
808 LFATAL("Mismatched tensor: " << jevois::dnn::shapestr(m) << " vs attr: " << jevois::dnn::shapestr(attr));
809
810 unsigned int const tt = jevois::dnn::vsi2cv(attr.dtype.vx_type);
811
812 switch (attr.dtype.qnt_type)
813 {
814 case VSI_NN_QNT_TYPE_NONE:
815 {
816 cv::Mat ret;
817 m.convertTo(ret, tt);
818 return ret;
819 }
820
821 case VSI_NN_QNT_TYPE_DFP:
822 {
823 switch (tt)
824 {
825 case CV_8S:
826 {
827 if (attr.dtype.fl > 7) LFATAL("Invalid DFP fl value " << attr.dtype.fl << ": must be in [0..7]");
828 cv::Mat ret;
829 m.convertTo(ret, tt, 1 << attr.dtype.fl, 0.0);
830 return ret;
831 }
832 case CV_16S:
833 {
834 if (attr.dtype.fl > 15) LFATAL("Invalid DFP fl value " << attr.dtype.fl << ": must be in [0..15]");
835 cv::Mat ret;
836 m.convertTo(ret, tt, 1 << attr.dtype.fl, 0.0);
837 return ret;
838 }
839 default: break; // will LFATAL() below
840 }
841 break;
842 }
843
844 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
845 {
846 switch (tt)
847 {
848 case CV_8U:
849 {
850 cv::Mat ret;
851 if (attr.dtype.scale == 0.0) LFATAL("Quantization scale must not be zero in " << jevois::dnn::shapestr(attr));
852 m.convertTo(ret, tt, 1.0 / attr.dtype.scale, attr.dtype.zero_point);
853 return ret;
854 }
855
856 default: break; // will LFATAL() below
857 }
858 break;
859 }
860
861 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC:
862 LFATAL("Affine per-channel symmetric not supported yet");
863
864 default: break; // will LFATAL() below
865 }
866
867 LFATAL("Quantization to " << jevois::dnn::shapestr(attr) << " not yet supported");
868}
869
870// ##############################################################################################################
871cv::Mat jevois::dnn::dequantize(cv::Mat const & m, vsi_nn_tensor_attr_t const & attr)
872{
873 if (! jevois::dnn::attrmatch(attr, m))
874 LFATAL("Mismatched tensor: " << jevois::dnn::shapestr(m) << " vs attr: " << jevois::dnn::shapestr(attr));
875
876 switch (attr.dtype.qnt_type)
877 {
878 case VSI_NN_QNT_TYPE_NONE:
879 {
880 cv::Mat ret;
881 m.convertTo(ret, CV_32F);
882 return ret;
883 }
884
885 case VSI_NN_QNT_TYPE_DFP:
886 {
887 cv::Mat ret;
888 m.convertTo(ret, CV_32F, 1.0 / (1 << attr.dtype.fl), 0.0);
889 return ret;
890 }
891
892 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
893 {
894 cv::Mat ret;
895 double const alpha = attr.dtype.scale;
896 double const beta = - alpha * attr.dtype.zero_point;
897 m.convertTo(ret, CV_32F, alpha, beta);
898 return ret;
899 }
900
901 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC:
902 LFATAL("Affine per-channel symmetric not supported yet");
903
904 default:
905 LFATAL("Unknown quantization type " << int(attr.dtype.qnt_type));
906 }
907}
908
909// ##############################################################################################################
910size_t jevois::dnn::effectiveDims(cv::Mat const & m)
911{
912 cv::MatSize const & rs = m.size;
913 size_t const ndims = rs.dims();
914 size_t ret = ndims;
915 for (size_t i = 0; i < ndims; ++i) if (rs[i] == 1) --ret; else break;
916 return ret;
917}
918
919// ##############################################################################################################
920cv::Mat jevois::dnn::concatenate(std::vector<cv::Mat> const & tensors, int axis)
921{
922 if (tensors.empty()) return cv::Mat();
923 if (tensors.size() == 1) return tensors[0];
924
925 cv::MatSize const & ms = tensors[0].size;
926 int const ndims = ms.dims();
927 auto const typ = tensors[0].type();
928
929 // Convert negative axis to positive and check within bounds:
930 if (axis < - ndims || axis >= ndims)
931 LFATAL("Incorrect axis " << axis << ": must be in [" << -ndims << " ... " << ndims - 1 << ']');
932 if (axis < 0) axis = ndims - axis;
933
934 // Check number of dimensions and data type; compute new size along concatenated axis:
935 size_t newsize = tensors[0].size[axis];
936
937 for (size_t i = 1; i < tensors.size(); ++i)
938 {
939 if (tensors[i].type() != typ)
940 LFATAL("Mismatched tensor types: tensors[0] is " << jevois::cvtypestr(typ) << " while tensors[" << i <<
941 "] is " << jevois::cvtypestr(tensors[i].type()));
942
943 if (tensors[i].size.dims() != ndims)
944 LFATAL("Mismatched number of dimensions: " << ndims << " for tensors[0] vs. " <<
945 tensors[i].size.dims() << " for tensors[" << i << ']');
946
947 newsize += tensors[i].size[axis];
948 }
949
950 // Check that all other dims match:
951 for (int a = 0; a < ndims; ++a)
952 if (a != axis)
953 for (size_t i = 1; i < tensors.size(); ++i)
954 if (tensors[i].size[a] != ms[a])
955 LFATAL("Mismatched size for axis " << a << ": tensors[0] has " << ms[a] << " while tensors[" <<
956 i << "] has " << tensors[i].size[a]);
957
958 // Ready to go. Caution: copying a cv::MatSize does not copy its array of dims:
959 int newdims[ndims]; for (int i = 0; i < ndims; ++i) newdims[i] = ms.p[i];
960 newdims[axis] = newsize;
961 cv::Mat ret(ndims, newdims, typ);
962 unsigned char * optr = ret.data;
963
964 size_t numcopy = 1; for (int a = 0; a < axis; ++a) numcopy *= ms[a];
965 size_t elemsize = jevois::cvBytesPerPix(typ); for (int a = axis + 1; a < ndims; ++a) elemsize *= ms[a];
966
967 for (size_t n = 0; n < numcopy; ++n)
968 for (size_t i = 0; i < tensors.size(); ++i)
969 {
970 size_t const axsize = tensors[i].size[axis];
971 unsigned char const * sptr = tensors[i].data + n * elemsize * axsize;
972 std::memcpy(optr, sptr, elemsize * axsize);
973 optr += elemsize * axsize;
974 }
975
976 return ret;
977}
978
979// ##############################################################################################################
980std::vector<cv::Mat> jevois::dnn::split(cv::Mat const & tensor, int axis, std::vector<int> const & sizes)
981{
982 cv::MatSize const & ms = tensor.size;
983 int const ndims = ms.dims();
984 auto const typ = tensor.type();
985 int const nsplit = sizes.size();
986
987 // Convert negative axis to positive and check within bounds:
988 if (axis < - ndims || axis >= ndims)
989 LFATAL("Incorrect axis " << axis << ": must be in [" << -ndims << " ... " << ndims - 1 <<
990 " for given tensor " << jevois::dnn::shapestr(tensor));
991 if (axis < 0) axis = ndims - axis;
992
993 // Handle trivial cases:
994 std::vector<cv::Mat> ret;
995 if (nsplit == 0) return ret;
996 if (nsplit == 1)
997 {
998 if (sizes[0] == ms[axis]) { ret.emplace_back(tensor); return ret; }
999 else LFATAL("Desired new size " << sizes[0] << " for axis " << axis << " with only one output tensor must match "
1000 "source size for that axis, but source is " << jevois::dnn::shapestr(tensor));
1001 }
1002
1003 // Check that all given sizes add up, allocate mats, sizes, out pointers:
1004 unsigned char * optr[nsplit]; size_t copysize[nsplit];
1005 int sum = 0;
1006 size_t numcopy = 1; for (int a = 0; a < axis; ++a) numcopy *= ms[a];
1007 size_t elemsize = jevois::cvBytesPerPix(typ); for (int a = axis + 1; a < ndims; ++a) elemsize *= ms[a];
1008
1009 // Caution: copying a cv::MatSize does not copy its array of dims
1010 int newdims[ndims]; for (int i = 0; i < ndims; ++i) newdims[i] = ms.p[i];
1011
1012 // Do the split:
1013 for (int i = 0; i < nsplit; ++i)
1014 {
1015 int const s = sizes[i];
1016 newdims[axis] = s; ret.emplace_back(cv::Mat(ndims, newdims, typ));
1017 optr[i] = ret.back().data;
1018 copysize[i] = s * elemsize;
1019 sum += s;
1020 }
1021
1022 if (sum != ms[axis])
1023 LFATAL("Given sizes [" << jevois::join(sizes, ", ") << "] do not add up to original size of axis " <<
1024 axis << " for tensor " << jevois::dnn::shapestr(tensor));
1025
1026 // Good to go, split it, we have at least 2 output tensors at this point:
1027 unsigned char const * sptr = tensor.data;
1028 for (size_t n = 0; n < numcopy; ++n)
1029 for (int j = 0; j < nsplit; ++j)
1030 {
1031 size_t const cs = copysize[j];
1032 std::memcpy(optr[j], sptr, cs);
1033 sptr += cs; optr[j] += cs;
1034 }
1035
1036 return ret;
1037}
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
Definition Log.H:211
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition Log.H:194
float fastexp(float x)
Compute fast exponential using approximation formula.
int tf2cv(TfLiteType t)
Convert from TensorFlow data type to OpenCV.
Definition Utils.C:310
int vsi2cv(vsi_nn_type_e t)
Convert from NPU data type to OpenCV.
Definition Utils.C:332
size_t softmax(float const *input, size_t const n, size_t const stride, float const fac, float *output, bool maxonly)
Apply softmax to a float vector.
Definition Utils.C:716
vsi_nn_tensor_attr_t tensorattr(TfLiteTensor const *t)
Get tensor shape and type attributes for a TensorFlow Lite tensor.
Definition Utils.C:588
std::string getLabel(std::map< int, std::string > const &labels, int id, bool namedonly=false)
Get a label from an id.
Definition Utils.C:68
cv::Mat quantize(cv::Mat const &m, vsi_nn_tensor_attr_t const &attr)
Quantize from float32 to fixed-point according to the quantization spec in attr.
Definition Utils.C:799
std::map< int, std::string > readLabelsFile(std::string const &fname)
Read a label file.
Definition Utils.C:25
float sigmoid(float x)
Compute sigmoid using fastexp.
vsi_nn_type_e onnx2vsi(ONNXTensorElementDataType t)
Convert from ONNX-Runtime data type to vsi_nn.
Definition Utils.C:232
std::vector< cv::Mat > split(cv::Mat const &tensor, int axis, std::vector< int > const &sizes)
Split a tensor into several, along a given axis.
Definition Utils.C:980
std::vector< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
Definition Utils.C:411
void clamp(cv::Rect &r, int width, int height)
Clamp a rectangle to within given image width and height.
Definition Utils.C:391
std::string attrstr(vsi_nn_tensor_attr_t const &attr)
Get a string describing the specs of a tensor, including quantification specs (not provided by shapes...
Definition Utils.C:536
float softmax_dfl(float const *src, float *dst, size_t const n, size_t const stride=1)
Compute softmax and return DFL distance.
Definition Utils.C:752
cv::Mat attrmat(vsi_nn_tensor_attr_t const &attr, void *dataptr=nullptr)
Construct a cv::Mat from attr and possibly data pointer.
Definition Utils.C:495
size_t effectiveDims(cv::Mat const &m)
Returns the number of non-unit dims in a cv::Mat.
Definition Utils.C:910
vsi_nn_type_e hailo2vsi(hailo_format_type_t t)
Convert from Hailo data type to vsi_nn.
Definition Utils.C:377
cv::Mat concatenate(std::vector< cv::Mat > const &tensors, int axis)
Concatenate several tensors into one.
Definition Utils.C:920
int stringToRGBA(std::string const &label, unsigned char alpha=128)
Compute a color from a label name.
Definition Utils.C:80
cv::Size attrsize(vsi_nn_tensor_attr_t const &attr)
Get a tensor's (width, height) size in cv::Size format, skipping over other dimensions.
Definition Utils.C:511
cv::Mat dequantize(cv::Mat const &m, vsi_nn_tensor_attr_t const &attr)
Dequantize an output to float32 according to the quantization spec in attr.
Definition Utils.C:871
vsi_nn_type_e tf2vsi(TfLiteType t)
Convert from TensorFlow data type to vsi_nn.
Definition Utils.C:354
void topK(float const *pfProb, float *pfMaxProb, uint32_t *pMaxClass, uint32_t outputCount, uint32_t topNum)
Get top-k entries and their indices.
Definition Utils.C:89
std::string shapestr(cv::Mat const &m)
Get a string of the form: "nD AxBxC... TYPE" from an n-dimensional cv::Mat with data type TYPE.
Definition Utils.C:109
std::vector< int > attrdims(vsi_nn_tensor_attr_t const &attr)
Get a tensor dims as a vector of int, useful to construct a matching cv::Mat.
Definition Utils.C:502
std::vector< size_t > strshape(std::string const &str)
Get a vector of size_t from a string containing AxBxC...
Definition Utils.C:301
bool attrmatch(vsi_nn_tensor_attr_t const &attr, cv::Mat const &blob)
Check that a cv::Mat blob matches exactly the spec of an attr.
Definition Utils.C:785
unsigned int cvBytesPerPix(unsigned int cvtype)
Return the number of bytes per pixel for a given OpenCV pixel type.
Definition Utils.C:89
std::string cvtypestr(unsigned int cvtype)
Convert cv::Mat::type() code to to a string (e.g., CV_8UC1, CV_32SC3, etc)
Definition Utils.C:58
std::string join(std::vector< T > const &tokens, std::string const &delimiter)
Concatenate a vector of tokens into a string.
size_t replaceStringAll(std::string &str, std::string const &from, std::string const &to)
Replace all instances of 'from' with 'to'.
Definition Utils.C:345
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition Utils.C:270