JeVois  1.21
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)
69{
70 auto itr = labels.find(id);
71 if (itr == labels.end()) return std::to_string(id);
72 return itr->second;
73}
74
75// ##############################################################################################################
76int jevois::dnn::stringToRGBA(std::string const & label, unsigned char alpha)
77{
78 int col = 0x80808080;
79 for (char const c : label) col = c + ((col << 5) - col);
80 col = (col & 0xffffff) | (alpha << 24);
81 return col;
82}
83
84// ##############################################################################################################
85void jevois::dnn::topK(float const * pfProb, float * pfMaxProb, uint32_t * pMaxClass, uint32_t outputCount,
86 uint32_t topNum)
87{
88 memset(pfMaxProb, 0xfe, sizeof(float) * topNum);
89 memset(pMaxClass, 0xff, sizeof(float) * topNum);
90
91 for (uint32_t j = 0; j < topNum; ++j)
92 {
93 for (uint32_t i = 0; i < outputCount; ++i)
94 {
95 uint32_t k;
96 for (k = 0; k < topNum; ++k) if (i == pMaxClass[k]) break;
97 if (k != topNum) continue;
98
99 if (pfProb[i] > pfMaxProb[j]) { pfMaxProb[j] = pfProb[i]; pMaxClass[j] = i; }
100 }
101 }
102}
103
104// ##############################################################################################################
105std::string jevois::dnn::shapestr(cv::Mat const & m)
106{
107 cv::MatSize const & ms = m.size; int const nd = ms.dims();
108 std::string ret = std::to_string(nd) + "D ";
109 for (int i = 0; i < nd; ++i) ret += std::to_string(ms[i]) + (i < nd-1 ? "x" : "");
110 ret += ' ' + jevois::cvtypestr(m.type());
111 return ret;
112}
113
114// ##############################################################################################################
115std::string jevois::dnn::shapestr(TfLiteTensor const * t)
116{
117
118 TfLiteIntArray const & dims = *t->dims;
119 std::string ret = std::to_string(dims.size) + "D ";
120 for (int i = 0; i < dims.size; ++i) ret += std::to_string(dims.data[i]) + (i < dims.size-1 ? "x" : "");
121
122 // Do not use TfLiteTypeGetName() as it returns different names...
123 switch (t->type)
124 {
125 case kTfLiteNoType: ret += " NoType"; break;
126 case kTfLiteFloat32: ret += " 32F"; break;
127 case kTfLiteInt32: ret += " 32S"; break;
128 case kTfLiteUInt8: ret += " 8U"; break;
129 case kTfLiteInt64: ret += " 64S"; break;
130 case kTfLiteString: ret += " String"; break;
131 case kTfLiteBool: ret += " 8B"; break;
132 case kTfLiteInt16: ret += " 16S"; break;
133 case kTfLiteComplex64: ret += " 64C"; break;
134 case kTfLiteInt8: ret += " 8I"; break;
135 case kTfLiteFloat16: ret += " 16F"; break;
136 case kTfLiteFloat64: ret += " 64F"; break;
137 case kTfLiteComplex128: ret += " 128C"; break;
138 default: ret += " UnknownType"; break;
139 }
140 return ret;
141}
142
143// ##############################################################################################################
144std::string jevois::dnn::shapestr(vsi_nn_tensor_attr_t const & attr)
145{
146 std::string ret = std::to_string(attr.dim_num) + "D ";
147 for (uint32_t i = 0; i < attr.dim_num; ++i)
148 ret += std::to_string(attr.size[attr.dim_num-1-i]) + (i < attr.dim_num-1 ? "x" : "");
149
150 // Value type:
151 switch (attr.dtype.vx_type)
152 {
153 case VSI_NN_TYPE_UINT8: ret += " 8U"; break;
154 case VSI_NN_TYPE_INT8: ret += " 8S"; break;
155 case VSI_NN_TYPE_BOOL8: ret += " 8B"; break;
156 case VSI_NN_TYPE_UINT16: ret += " 16U"; break;
157 case VSI_NN_TYPE_INT16: ret += " 16S"; break;
158 case VSI_NN_TYPE_FLOAT16: ret += " 16F"; break;
159 case VSI_NN_TYPE_BFLOAT16: ret += " 16B"; break;
160 case VSI_NN_TYPE_UINT32: ret += " 32U"; break;
161 case VSI_NN_TYPE_INT32: ret += " 32S"; break;
162 case VSI_NN_TYPE_FLOAT32: ret += " 32F"; break;
163 case VSI_NN_TYPE_UINT64: ret += " 64U"; break;
164 case VSI_NN_TYPE_INT64: ret += " 64S"; break;
165 case VSI_NN_TYPE_FLOAT64: ret += " 64F"; break;
166 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(attr.dtype.vx_type));
167 }
168
169 return ret;
170}
171
172#ifdef JEVOIS_PRO
173// ##############################################################################################################
174std::string jevois::dnn::shapestr(Ort::ConstTensorTypeAndShapeInfo const & ti)
175{
176 std::ostringstream os;
177 std::vector<int64_t> input_node_dims = ti.GetShape();
178 os << input_node_dims.size() << "D ";
179 for (int64_t d : input_node_dims) os << d << 'x';
180 os.seekp(-1, os.cur); // will overwrite last 'x'
181
182 switch (ti.GetElementType())
183 {
184 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED: os << " UNDEFINED"; break;
185 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: os << " 32F"; break;
186 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: os << " 8U"; break;
187 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: os << " 8S"; break;
188 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: os << " 16U"; break;
189 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: os << " 16S"; break;
190 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: os << " 32S"; break;
191 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: os << " 64S"; break;
192 case ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING: os << " STR"; break;
193 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: os << " BOOL"; break;
194 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: os << " 16F"; break;
195 case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: os << " 64F"; break;
196 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: os << " 32U"; break;
197 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: os << " 64U"; break;
198 case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64: os << " 64CPLX"; break;
199 case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128: os << " 128CPLX"; break;
200 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16: os << " 16B"; break;
201 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(ti.GetElementType()));
202 }
203
204 return os.str();
205}
206
207// ##############################################################################################################
208vsi_nn_type_e jevois::dnn::onnx2vsi(ONNXTensorElementDataType t)
209{
210 switch (t)
211 {
212 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED: return VSI_NN_TYPE_NONE;
213 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return VSI_NN_TYPE_FLOAT32;
214 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: return VSI_NN_TYPE_UINT8;
215 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8: return VSI_NN_TYPE_INT8;
216 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16: return VSI_NN_TYPE_UINT16;
217 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16: return VSI_NN_TYPE_INT16;
218 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return VSI_NN_TYPE_INT32;
219 case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return VSI_NN_TYPE_INT64;
220 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL: return VSI_NN_TYPE_BOOL8;
221 case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: return VSI_NN_TYPE_FLOAT16;
222 case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: return VSI_NN_TYPE_FLOAT64;
223 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: return VSI_NN_TYPE_UINT32;
224 case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: return VSI_NN_TYPE_UINT64;
225 case ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16: return VSI_NN_TYPE_BFLOAT16;
226 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING: // unsupported by VSI
227 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64:
228 //case ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128:
229 default: throw std::range_error("onnx2vsi: Unsupported tensor type " + std::to_string(t));
230 }
231}
232
233// ##############################################################################################################
234vsi_nn_tensor_attr_t jevois::dnn::tensorattr(Ort::ConstTensorTypeAndShapeInfo const & ti)
235{
236 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
237 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
238 attr.dtype.vx_type = jevois::dnn::onnx2vsi(ti.GetElementType());
239
240 std::vector<int64_t> const dims = ti.GetShape();
241 size_t const ds = dims.size();
242 attr.dim_num = ds;
243 for (size_t i = 0; i < ds; ++i) attr.size[ds - 1 - i] = dims[i];
244
245 // FIXME: quantization not yet supported
246 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
247
248 return attr;
249}
250
251// ##############################################################################################################
252std::string jevois::dnn::shapestr(hailo_vstream_info_t const & vi)
253{
254 // FIXME: should optimize but beware that vi.shape may have different interpretations depending on NCHW vs NWCH etc
255 return shapestr(tensorattr(vi));
256
257 /*
258 std::string ret = "3D ";
259 switch (+ std::to_string(vi.shape[0]) + 'x' +
260 std::to_string(vi.shape[1]) + 'x' + std::to_string(vi.shape[2]);
261
262 switch (vi.format.type)
263 {
264 case HAILO_FORMAT_TYPE_AUTO: ret += " AUTO"; break;
265 case HAILO_FORMAT_TYPE_UINT8: ret += " 8U"; break;
266 case HAILO_FORMAT_TYPE_UINT16: ret += " 16U"; break;
267 case HAILO_FORMAT_TYPE_FLOAT32: ret += " 32F"; break;
268 default: throw std::range_error("shapestr: Unsupported tensor type " + std::to_string(vi.format));
269 }
270
271 return ret;
272 */
273}
274#endif
275
276// ##############################################################################################################
277std::vector<size_t> jevois::dnn::strshape(std::string const & str)
278{
279 std::vector<size_t> ret;
280 auto tok = jevois::split(str, "x");
281 for (std::string const & t : tok) ret.emplace_back(std::stoi(t));
282 return ret;
283}
284
285// ##############################################################################################################
286int jevois::dnn::tf2cv(TfLiteType t)
287{
288 switch (t)
289 {
290 case kTfLiteFloat32: return CV_32F;
291 case kTfLiteInt32: return CV_32S;
292 case kTfLiteUInt8: return CV_8U;
293 case kTfLiteInt16: return CV_16S;
294 case kTfLiteInt8: return CV_8S;
295 case kTfLiteFloat16: return CV_16F;
296 case kTfLiteFloat64: return CV_64F;
297 //case kTfLiteComplex128:
298 //case kTfLiteComplex64:
299 //case kTfLiteBool:
300 //case kTfLiteString:
301 //case kTfLiteInt64:
302 //case kTfLiteNoType:
303 default: throw std::range_error(std::string("tf2cv: Unsupported type ") + TfLiteTypeGetName(t));
304 }
305}
306
307// ##############################################################################################################
308int jevois::dnn::vsi2cv(vsi_nn_type_e t)
309{
310 switch (t)
311 {
312 case VSI_NN_TYPE_UINT8: return CV_8U;
313 case VSI_NN_TYPE_INT8: return CV_8S;
314 case VSI_NN_TYPE_BOOL8: return CV_8U;
315 case VSI_NN_TYPE_UINT16: return CV_16U;
316 case VSI_NN_TYPE_INT16: return CV_16S;
317 case VSI_NN_TYPE_FLOAT16: return CV_16F;
318 //case VSI_NN_TYPE_BFLOAT16: return CV_16F; // check
319 //case VSI_NN_TYPE_UINT32: return CV_32U; // unsupported by opencv
320 case VSI_NN_TYPE_INT32: return CV_32S;
321 case VSI_NN_TYPE_FLOAT32: return CV_32F;
322 //case VSI_NN_TYPE_UINT64: return CV_64U; // unsupported by opencv
323 //case VSI_NN_TYPE_INT64: return CV_64S; // unsupported by opencv
324 case VSI_NN_TYPE_FLOAT64: return CV_64F;
325 default: throw std::range_error("vsi2cv: Unsupported tensor type " + std::to_string(t));
326 }
327}
328
329// ##############################################################################################################
330vsi_nn_type_e jevois::dnn::tf2vsi(TfLiteType t)
331{
332 switch (t)
333 {
334 case kTfLiteFloat32: return VSI_NN_TYPE_FLOAT32;
335 case kTfLiteInt32: return VSI_NN_TYPE_INT32;
336 case kTfLiteUInt8: return VSI_NN_TYPE_UINT8;
337 case kTfLiteInt16: return VSI_NN_TYPE_INT16;
338 case kTfLiteInt8: return VSI_NN_TYPE_INT8;
339 case kTfLiteFloat16: return VSI_NN_TYPE_FLOAT16;
340 case kTfLiteFloat64: return VSI_NN_TYPE_FLOAT64;
341 case kTfLiteInt64: return VSI_NN_TYPE_INT64;
342 case kTfLiteBool: return VSI_NN_TYPE_BOOL8; // fixme: need to check
343 case kTfLiteNoType: return VSI_NN_TYPE_NONE;
344 //case kTfLiteComplex128:
345 //case kTfLiteComplex64:
346 //case kTfLiteString:
347 default: throw std::range_error(std::string("tf2vsi: Unsupported type ") + TfLiteTypeGetName(t));
348 }
349}
350
351// ##############################################################################################################
352#ifdef JEVOIS_PRO
353vsi_nn_type_e jevois::dnn::hailo2vsi(hailo_format_type_t t)
354{
355 switch (t)
356 {
357 case HAILO_FORMAT_TYPE_AUTO: return VSI_NN_TYPE_NONE; break; // or throw?
358 case HAILO_FORMAT_TYPE_UINT8: return VSI_NN_TYPE_UINT8; break;
359 case HAILO_FORMAT_TYPE_UINT16: return VSI_NN_TYPE_UINT16; break;
360 case HAILO_FORMAT_TYPE_FLOAT32: return VSI_NN_TYPE_FLOAT32; break;
361 default: throw std::range_error("hailo2vsi: Unsupported tensor type " + std::to_string(t));
362 }
363}
364#endif
365
366// ##############################################################################################################
367void jevois::dnn::clamp(cv::Rect & r, int width, int height)
368{
369 int tx = std::min(width - 1, std::max(0, r.x));
370 int ty = std::min(height - 1, std::max(0, r.y));
371 int bx = std::min(width - 1, std::max(0, r.x + r.width));
372 int by = std::min(height - 1, std::max(0, r.y + r.height));
373 r.x = tx; r.y = ty; r.width = bx - tx; r.height = by - ty;
374}
375
376// ##############################################################################################################
377void jevois::dnn::clamp(cv::Rect2f & r, float width, float height)
378{
379 float tx = std::min(width - 1.0F, std::max(0.0F, r.x));
380 float ty = std::min(height - 1.0F, std::max(0.0F, r.y));
381 float bx = std::min(width - 1.0F, std::max(0.0F, r.x + r.width));
382 float by = std::min(height - 1.0F, std::max(0.0F, r.y + r.height));
383 r.x = tx; r.y = ty; r.width = bx - tx; r.height = by - ty;
384}
385
386// ##############################################################################################################
387std::vector<vsi_nn_tensor_attr_t> jevois::dnn::parseTensorSpecs(std::string const & specs)
388{
389 char const * const specdef = "[NCHW:|NHWC:|NA:|AUTO:]Type:[NxCxHxW|NxHxWxC|...][:QNT[:fl|:scale:zero]]";
390 std::vector<std::string> spectok = jevois::split(specs, ",\\s*");
391 std::vector<vsi_nn_tensor_attr_t> ret;
392
393 for (std::string const & spec : spectok)
394 {
395 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
396
397 // NCHW:Type:NxCxHxW:QNT:scale:mean
398 std::vector<std::string> tok = jevois::split(spec, ":");
399 if (tok.size() < 2) throw std::runtime_error("parseTensorSpecs: Malformed tensor spec ["+spec+"] not "+specdef);
400
401 // Decode optional shape:
402 size_t n = 0; // next tok to parse
403 if (tok[0] == "NCHW") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW; } // planar RGB
404 else if (tok[0] == "NHWC") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC; } // packed RGB
405 else if (tok[0] == "NA") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_NA; }
406 else if (tok[0] == "AUTO") { ++n; attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO; }
407 else attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO; // use AUTO if it was not given
408
409 // We need at least type and dims:
410 if (tok.size() < n+2) throw std::runtime_error("parseTensorSpecs: Malformed tensor spec ["+spec+"] not "+specdef);
411
412 // Decode type and convert to vsi:
413 if (tok[n] == "8U") attr.dtype.vx_type = VSI_NN_TYPE_UINT8;
414 else if (tok[n] == "8S") attr.dtype.vx_type = VSI_NN_TYPE_INT8;
415 else if (tok[n] == "8B") attr.dtype.vx_type = VSI_NN_TYPE_BOOL8;
416 else if (tok[n] == "16U") attr.dtype.vx_type = VSI_NN_TYPE_UINT16;
417 else if (tok[n] == "16S") attr.dtype.vx_type = VSI_NN_TYPE_INT16;
418 else if (tok[n] == "16F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT16;
419 else if (tok[n] == "16B") attr.dtype.vx_type = VSI_NN_TYPE_BFLOAT16;
420 else if (tok[n] == "32U") attr.dtype.vx_type = VSI_NN_TYPE_UINT32;
421 else if (tok[n] == "32S") attr.dtype.vx_type = VSI_NN_TYPE_INT32;
422 else if (tok[n] == "32F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT32;
423 else if (tok[n] == "64U") attr.dtype.vx_type = VSI_NN_TYPE_UINT64;
424 else if (tok[n] == "64S") attr.dtype.vx_type = VSI_NN_TYPE_INT64;
425 else if (tok[n] == "64F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT64;
426 else throw std::range_error("parseTensorSpecs: Invalid tensor type [" + tok[n] + "] in " + spec);
427 ++n; // next token
428
429 // Decode the dims:
430 std::vector<size_t> dims = jevois::dnn::strshape(tok[n]);
431 attr.dim_num = dims.size();
432 for (size_t i = 0; i < attr.dim_num; ++i) attr.size[attr.dim_num - 1 - i] = dims[i];
433 ++n; // next token
434
435 // Decode optional quantization type and its possible extra parameters:
436 if (n == tok.size() || tok[n] == "NONE")
437 {
438 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
439 }
440 else if (tok[n] == "DFP")
441 {
442 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_DFP;
443 if (tok.size() != n+2)
444 throw std::range_error("parseTensorSpecs: In "+spec+", DFP quantization needs :fl (" + specdef + ')');
445 attr.dtype.fl = std::stoi(tok[n+1]);
446 }
447
448 else if (tok[n] == "AA" || tok[n] == "AS") // affine asymmetric and symmetric same, see ovxlib/vsi_nn_tensor.h
449 {
450 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
451 if (tok.size() != n+3)
452 throw std::range_error("parseTensorSpecs: In "+spec+", AA/AS quantization needs :scale:zero ("+specdef+')');
453 attr.dtype.scale = std::stof(tok[n+1]);
454 attr.dtype.zero_point = std::stoi(tok[n+2]);
455 }
456 else if (tok[n] == "APS")
457 {
458 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC;
459 throw std::range_error("parseTensorSpecs: In " + spec + ", AFFINE_PERCHANNEL_SYMMETRIC quant not yet supported");
460 }
461 else throw std::range_error("parseTensorSpecs: Invalid quantization type in " + spec);
462
463 // Done with this tensor:
464 ret.emplace_back(attr);
465 }
466
467 return ret;
468}
469
470// ##############################################################################################################
471cv::Mat jevois::dnn::attrmat(vsi_nn_tensor_attr_t const & attr, void * dataptr)
472{
473 if (dataptr) return cv::Mat(jevois::dnn::attrdims(attr), jevois::dnn::vsi2cv(attr.dtype.vx_type), dataptr);
474 else return cv::Mat(jevois::dnn::attrdims(attr), jevois::dnn::vsi2cv(attr.dtype.vx_type));
475}
476
477// ##############################################################################################################
478std::vector<int> jevois::dnn::attrdims(vsi_nn_tensor_attr_t const & attr)
479{
480 size_t const ndim = attr.dim_num;
481 std::vector<int> cvdims(ndim);
482 for (size_t i = 0; i < ndim; ++i) cvdims[ndim - 1 - i] = attr.size[i];
483 return cvdims;
484}
485
486// ##############################################################################################################
487cv::Size jevois::dnn::attrsize(vsi_nn_tensor_attr_t const & attr)
488{
489 switch (attr.dtype.fmt)
490 {
491 case VSI_NN_DIM_FMT_NHWC:
492 if (attr.dim_num < 3) throw std::range_error("attrsize: need at least 3D, got " + jevois::dnn::attrstr(attr));
493 return cv::Size(attr.size[1], attr.size[2]);
494
495 case VSI_NN_DIM_FMT_NCHW:
496 if (attr.dim_num < 2) throw std::range_error("attrsize: need at least 2D, got " + jevois::dnn::attrstr(attr));
497 return cv::Size(attr.size[0], attr.size[1]);
498
499 case VSI_NN_DIM_FMT_AUTO:
500 if (attr.dim_num < 2) return cv::Size(attr.size[0], 1);
501 if (attr.dim_num < 3) return cv::Size(attr.size[0], attr.size[1]);
502 // ok, size[] starts with either CWH (when dim index goes 0..2) or WHC, assume C<H
503 if (attr.size[0] > attr.size[2]) return cv::Size(attr.size[0], attr.size[1]); // WHCN
504 else return cv::Size(attr.size[1], attr.size[2]); // CWHN
505
506 default:
507 throw std::range_error("attrsize: cannot extract width and height, got " + jevois::dnn::attrstr(attr));
508 }
509}
510
511// ##############################################################################################################
512std::string jevois::dnn::attrstr(vsi_nn_tensor_attr_t const & attr)
513{
514 std::string ret;
515
516 // Dimension ordering:
517 switch (attr.dtype.fmt)
518 {
519 case VSI_NN_DIM_FMT_NCHW: ret += "NCHW:"; break;
520 case VSI_NN_DIM_FMT_NHWC: ret += "NHWC:"; break;
521 default: break;
522 }
523
524 // Value type:
525 switch (attr.dtype.vx_type)
526 {
527 case VSI_NN_TYPE_UINT8: ret += "8U:"; break;
528 case VSI_NN_TYPE_INT8: ret += "8S:"; break;
529 case VSI_NN_TYPE_BOOL8: ret += "8B:"; break;
530 case VSI_NN_TYPE_UINT16: ret += "16U:"; break;
531 case VSI_NN_TYPE_INT16: ret += "16S:"; break;
532 case VSI_NN_TYPE_FLOAT16: ret += "16F:"; break;
533 case VSI_NN_TYPE_BFLOAT16: ret += "16B:"; break;
534 case VSI_NN_TYPE_UINT32: ret += "32U:"; break;
535 case VSI_NN_TYPE_INT32: ret += "32S:"; break;
536 case VSI_NN_TYPE_FLOAT32: ret += "32F:"; break;
537 case VSI_NN_TYPE_UINT64: ret += "64U:"; break;
538 case VSI_NN_TYPE_INT64: ret += "64S:"; break;
539 case VSI_NN_TYPE_FLOAT64: ret += "64F:"; break;
540 default: ret += "TYPE_UNKNOWN:";
541 }
542
543 // Dims:
544 for (uint32_t i = 0; i < attr.dim_num; ++i)
545 ret += std::to_string(attr.size[attr.dim_num - 1 - i]) + ((i<attr.dim_num-1) ? 'x' : ':');
546
547 // Quantization:
548 switch (attr.dtype.qnt_type)
549 {
550 case VSI_NN_QNT_TYPE_NONE: ret += "NONE"; break;
551 case VSI_NN_QNT_TYPE_DFP: ret += "DFP:" + std::to_string(attr.dtype.fl); break;
552 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
553 ret += "AA:" + std::to_string(attr.dtype.scale) + ':' + std::to_string(attr.dtype.zero_point);
554 break;
555 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC: ret += "APS:unsupported"; break;
556 default: ret += "QUANT_UNKNOWN";
557 }
558
559 return ret;
560}
561
562// ##############################################################################################################
563vsi_nn_tensor_attr_t jevois::dnn::tensorattr(TfLiteTensor const * t)
564{
565 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
566 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
567 attr.dtype.vx_type = jevois::dnn::tf2vsi(t->type);
568
569 switch (t->quantization.type)
570 {
571 case kTfLiteNoQuantization:
572 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
573 break;
574
575 case kTfLiteAffineQuantization:
576 {
577 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
578 attr.dtype.scale = t->params.scale;
579 attr.dtype.zero_point = t->params.zero_point;
580 }
581 break;
582
583 default: LFATAL("unsupported quantization " << t->quantization.type);
584 }
585
586 TfLiteIntArray const & dims = *t->dims;
587 attr.dim_num = dims.size;
588 for (int i = 0; i < dims.size; ++i) attr.size[dims.size - 1 - i] = dims.data[i];
589
590 // Set the fmt to NCHW or NHWC if possible:
591 if (attr.dim_num == 4)
592 {
593 if (attr.size[0] > attr.size[2]) attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW; // assume H>C
594 else attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
595 }
596
597 return attr;
598}
599
600// ##############################################################################################################
601#ifdef JEVOIS_PRO
602vsi_nn_tensor_attr_t jevois::dnn::tensorattr(hailo_vstream_info_t const & vi)
603{
604 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
605
606 attr.dtype.vx_type = hailo2vsi(vi.format.type);
607
608 switch (vi.format.order)
609 {
610 case HAILO_FORMAT_ORDER_HAILO_NMS:
611 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
612 attr.dim_num = 2;
613 attr.size[0] = vi.nms_shape.number_of_classes;
614 attr.size[1] = vi.nms_shape.max_bboxes_per_class * 5; // Each box has: xmin, ymin, xmax, ymax, score
615 break;
616
617 case HAILO_FORMAT_ORDER_NHWC:
618 case HAILO_FORMAT_ORDER_FCR:
619 case HAILO_FORMAT_ORDER_F8CR:
620 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
621 attr.dim_num = 4;
622 attr.size[0] = vi.shape.features;
623 attr.size[1] = vi.shape.width;
624 attr.size[2] = vi.shape.height;
625 attr.size[3] = 1;
626 break;
627
628 case HAILO_FORMAT_ORDER_NHW:
629 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
630 attr.dim_num = 4;
631 attr.size[0] = 1;
632 attr.size[1] = vi.shape.width;
633 attr.size[2] = vi.shape.height;
634 attr.size[3] = 1;
635 break;
636
637 case HAILO_FORMAT_ORDER_NC:
638 attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
639 attr.dim_num = 4;
640 attr.size[0] = 1;
641 attr.size[1] = 1;
642 attr.size[2] = vi.shape.features;
643 attr.size[3] = 1;
644 break;
645
646 case HAILO_FORMAT_ORDER_NCHW:
647 attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW;
648 attr.dim_num = 4;
649 attr.size[0] = vi.shape.features;
650 attr.size[1] = vi.shape.width;
651 attr.size[2] = vi.shape.height;
652 attr.size[3] = 1;
653 break;
654
655 default: throw std::range_error("tensorattr: Unsupported Hailo order " +std::to_string(vi.format.order));
656 }
657
658 // Hailo only supports one quantization type:
659 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC;
660 attr.dtype.scale = vi.quant_info.qp_scale;
661 attr.dtype.zero_point = int32_t(vi.quant_info.qp_zp);
662
663 return attr;
664}
665#endif
666
667// ##############################################################################################################
668size_t jevois::dnn::softmax(float const * input, size_t const n, size_t const stride, float const fac, float * output,
669 bool maxonly)
670{
671 if (stride == 0) LFATAL("Cannot work with stride = 0");
672
673 float sum = 0.0F;
674 float largest = -FLT_MAX; size_t largest_idx = 0;
675 size_t const ns = n * stride;
676
677 for (size_t i = 0; i < ns; i += stride) if (input[i] > largest) { largest = input[i]; largest_idx = i; }
678
679 if (fac == 1.0F)
680 for (size_t i = 0; i < ns; i += stride)
681 {
682 float const e = expf(input[i] - largest);
683 sum += e;
684 output[i] = e;
685 }
686 else
687 for (size_t i = 0; i < ns; i += stride)
688 {
689 float const e = expf(input[i]/fac - largest/fac);
690 sum += e;
691 output[i] = e;
692 }
693
694 if (sum)
695 {
696 if (maxonly) output[largest_idx] /= sum;
697 else for (size_t i = 0; i < ns; i += stride) output[i] /= sum;
698 }
699
700 return largest_idx;
701}
702
703// ##############################################################################################################
704bool jevois::dnn::attrmatch(vsi_nn_tensor_attr_t const & attr, cv::Mat const & blob)
705{
706 // Check that blob and tensor are a complete match:
707 if (blob.channels() != 1) return false;
708 if (blob.depth() != jevois::dnn::vsi2cv(attr.dtype.vx_type)) return false;
709 if (uint32_t(blob.size.dims()) != attr.dim_num) return false;
710
711 for (size_t i = 0; i < attr.dim_num; ++i)
712 if (int(attr.size[attr.dim_num - 1 - i]) != blob.size[i]) return false;
713
714 return true;
715}
716
717// ##############################################################################################################
718cv::Mat jevois::dnn::quantize(cv::Mat const & m, vsi_nn_tensor_attr_t const & attr)
719{
720 if (m.depth() != CV_32F) LFATAL("Tensor to quantize must be 32F");
721
722 // Do a sloppy match for total size only since m may still be 2D RGB packed vs 4D attr...
723 std::vector<int> adims = jevois::dnn::attrdims(attr);
724 size_t tot = 1; for (int d : adims) tot *= d;
725
726 if (tot != m.total() * m.channels())
727 LFATAL("Mismatched tensor: " << jevois::dnn::shapestr(m) << " vs attr: " << jevois::dnn::shapestr(attr));
728
729 unsigned int const tt = jevois::dnn::vsi2cv(attr.dtype.vx_type);
730
731 switch (attr.dtype.qnt_type)
732 {
733 case VSI_NN_QNT_TYPE_NONE:
734 {
735 cv::Mat ret;
736 m.convertTo(ret, tt);
737 return ret;
738 }
739
740 case VSI_NN_QNT_TYPE_DFP:
741 {
742 switch (tt)
743 {
744 case CV_8S:
745 {
746 if (attr.dtype.fl > 7) LFATAL("Invalid DFP fl value " << attr.dtype.fl << ": must be in [0..7]");
747 cv::Mat ret;
748 m.convertTo(ret, tt, 1 << attr.dtype.fl, 0.0);
749 return ret;
750 }
751 case CV_16S:
752 {
753 if (attr.dtype.fl > 15) LFATAL("Invalid DFP fl value " << attr.dtype.fl << ": must be in [0..15]");
754 cv::Mat ret;
755 m.convertTo(ret, tt, 1 << attr.dtype.fl, 0.0);
756 return ret;
757 }
758 default:
759 break;
760 }
761 break;
762 }
763
764 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
765 {
766 switch (tt)
767 {
768 case CV_8U:
769 {
770 cv::Mat ret;
771 if (attr.dtype.scale == 0.0) LFATAL("Quantization scale must not be zero in " << jevois::dnn::shapestr(attr));
772 m.convertTo(ret, tt, 1.0 / attr.dtype.scale, attr.dtype.zero_point);
773 return ret;
774 }
775
776 default:
777 break;
778 }
779 break;
780 }
781
782 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC:
783 LFATAL("Affine per-channel symmetric not supported yet");
784
785 default:
786 break;
787 }
788
789 LFATAL("Quantization to " << jevois::dnn::shapestr(attr) << " not yet supported");
790}
791
792// ##############################################################################################################
793cv::Mat jevois::dnn::dequantize(cv::Mat const & m, vsi_nn_tensor_attr_t const & attr)
794{
795 if (! jevois::dnn::attrmatch(attr, m))
796 LFATAL("Mismatched tensor: " << jevois::dnn::shapestr(m) << " vs attr: " << jevois::dnn::shapestr(attr));
797
798 switch (attr.dtype.qnt_type)
799 {
800 case VSI_NN_QNT_TYPE_NONE:
801 {
802 cv::Mat ret;
803 m.convertTo(ret, CV_32F);
804 return ret;
805 }
806
807 case VSI_NN_QNT_TYPE_DFP:
808 {
809 cv::Mat ret;
810 m.convertTo(ret, CV_32F, 1.0 / (1 << attr.dtype.fl), 0.0);
811 return ret;
812 }
813
814 case VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC: // same value as VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC:
815 {
816 cv::Mat ret;
817 m.convertTo(ret, CV_32F);
818 if (attr.dtype.zero_point) ret -= attr.dtype.zero_point;
819 if (attr.dtype.scale != 1.0F) ret *= attr.dtype.scale;
820 return ret;
821 }
822
823 case VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC:
824 LFATAL("Affine per-channel symmetric not supported yet");
825
826 default:
827 LFATAL("Unknown quantization type " << int(attr.dtype.qnt_type));
828 }
829}
830
831// ##############################################################################################################
832size_t jevois::dnn::effectiveDims(cv::Mat const & m)
833{
834 cv::MatSize const & rs = m.size;
835 size_t const ndims = rs.dims();
836 size_t ret = ndims;
837 for (size_t i = 0; i < ndims; ++i) if (rs[i] == 1) --ret; else break;
838 return ret;
839}
840
841// ##############################################################################################################
842cv::Mat jevois::dnn::concatenate(std::vector<cv::Mat> const & tensors, int axis)
843{
844 if (tensors.empty()) return cv::Mat();
845 if (tensors.size() == 1) return tensors[0];
846
847 cv::MatSize ms = tensors[0].size;
848 int const ndims = ms.dims();
849 auto const typ = tensors[0].type();
850
851 // Convert negative axis to positive and check within bounds:
852 if (axis < - ndims || axis >= ndims)
853 LFATAL("Incorrect axis " << axis << ": must be in [" << -ndims << " ... " << ndims - 1 << ']');
854 if (axis < 0) axis = ndims - axis;
855
856 // Check number of dimensions and data type; compute new size along concatenated axis:
857 size_t newsize = tensors[0].size[axis];
858
859 for (size_t i = 1; i < tensors.size(); ++i)
860 {
861 if (tensors[i].type() != typ)
862 LFATAL("Mismatched tensor types: tensors[0] is " << jevois::cvtypestr(typ) << " while tensors[" << i <<
863 "] is " << jevois::cvtypestr(tensors[i].type()));
864
865 if (tensors[i].size.dims() != ndims)
866 LFATAL("Mismatched number of dimensions: " << ndims << " for tensors[0] vs. " <<
867 tensors[i].size.dims() << " for tensors[" << i << ']');
868
869 newsize += tensors[i].size[axis];
870 }
871
872 // Check that all other dims match:
873 for (int a = 0; a < ndims; ++a)
874 if (a != axis)
875 for (size_t i = 1; i < tensors.size(); ++i)
876 if (tensors[i].size[a] != ms[a])
877 LFATAL("Mismatched size for axis " << a << ": tensors[0] has " << ms[a] << " while tensors[" <<
878 i << "] has " << tensors[i].size[a]);
879
880 // Ready to go:
881 ms[axis] = newsize;
882 cv::Mat ret(ndims, ms.p, typ);
883 unsigned char * optr = ret.data;
884
885 size_t numcopy = 1; for (int a = 0; a < axis; ++a) numcopy *= ms[a];
886 size_t elemsize = jevois::cvBytesPerPix(typ); for (int a = axis + 1; a < ndims; ++a) elemsize *= ms[a];
887
888 for (size_t n = 0; n < numcopy; ++n)
889 for (size_t i = 0; i < tensors.size(); ++i)
890 {
891 size_t const axsize = tensors[i].size[axis];
892 unsigned char const * sptr = tensors[i].data + n * elemsize * axsize;
893 std::memcpy(optr, sptr, elemsize * axsize);
894 optr += elemsize * axsize;
895 }
896
897 return ret;
898}
899
#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
int tf2cv(TfLiteType t)
Convert from TensorFlow data type to OpenCV.
Definition Utils.C:286
int vsi2cv(vsi_nn_type_e t)
Convert from NPU data type to OpenCV.
Definition Utils.C:308
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:668
vsi_nn_tensor_attr_t tensorattr(TfLiteTensor const *t)
Get tensor shape and type attributes for a TensorFlow Lite tensor.
Definition Utils.C:563
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:718
std::map< int, std::string > readLabelsFile(std::string const &fname)
Read a label file.
Definition Utils.C:25
vsi_nn_type_e onnx2vsi(ONNXTensorElementDataType t)
Convert from ONNX-Runtime data type to vsi_nn.
Definition Utils.C:208
std::vector< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
Definition Utils.C:387
void clamp(cv::Rect &r, int width, int height)
Clamp a rectangle to within given image width and height.
Definition Utils.C:367
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:512
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:471
size_t effectiveDims(cv::Mat const &m)
Returns the number of non-unit dims in a cv::Mat.
Definition Utils.C:832
vsi_nn_type_e hailo2vsi(hailo_format_type_t t)
Convert from Hailo data type to vsi_nn.
Definition Utils.C:353
cv::Mat concatenate(std::vector< cv::Mat > const &tensors, int axis)
Concatenate several tensors into one.
Definition Utils.C:842
int stringToRGBA(std::string const &label, unsigned char alpha=128)
Compute a color from a label name.
Definition Utils.C:76
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:487
std::string getLabel(std::map< int, std::string > const &labels, int id)
Get a label from an id.
Definition Utils.C:68
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:793
vsi_nn_type_e tf2vsi(TfLiteType t)
Convert from TensorFlow data type to vsi_nn.
Definition Utils.C:330
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:85
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:105
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:478
std::vector< size_t > strshape(std::string const &str)
Get a vector of size_t from a string containing AxBxC...
Definition Utils.C:277
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:704
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
size_t replaceStringAll(std::string &str, std::string const &from, std::string const &to)
Replace all instances of 'from' with 'to'.
Definition Utils.C:344
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