JeVois  1.23
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
Network.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2021 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/Network.H>
19#include <jevois/DNN/Utils.H>
20#include <jevois/Util/Async.H>
21#include <jevois/Debug/Timer.H>
23
24// Special output tensor number that means apply transform to all output tensors:
25#define ALL_TENSORS 12345678
26
27// ####################################################################################################
30
31// ####################################################################################################
33{
34 comment::freeze(doit);
35 url::freeze(doit);
36 extraintensors::freeze(doit);
37}
38
39// ####################################################################################################
40void jevois::dnn::Network::onParamChange(network::outtransform const &, std::string const & val)
41{
42 itsOps.clear();
43 if (val.empty()) return;
44
45 // Split sequence by semi-colon:
46 std::vector<std::string> ops = jevois::split(val, "\\s*;\\s*");
47
48 // Decode each operation as op(arg1, arg2, ...):
49 for (std::string const & op : ops)
50 {
51 Oper o; bool syntax_error = true;
52 std::vector<std::string> tok = jevois::split(op, "(\\s*\\(\\s*|\\s*,\\s*|\\s*\\)\\s*)");
53 //LINFO("op=["<<op<<"] and tok="<<jevois::join(tok, "/"));
54
55 // ----------------------------------------------------------------------------------------------------
56 if (tok[0] == "shape")
57 {
58 if (tok.size() == 3)
59 try
60 {
61 o.op = Operator::Shape;
62 o.tnum.emplace_back(std::stoul(tok[1]));
63 std::vector<size_t> const newshape = jevois::dnn::strshape(tok[2]);
64 for (size_t v : newshape) o.newvals.emplace_back(int(v));
65 syntax_error = false;
66 } catch (...) { }
67
68 if (syntax_error) LFATAL("Syntax error, expected: shape(outnum, AxBxC...)");
69 }
70 // ----------------------------------------------------------------------------------------------------
71 else if (tok[0] == "transpose")
72 {
73 if (tok.size() >= 3)
74 try
75 {
76 o.op = Operator::Transpose;
77 if (tok[1] == "*") o.tnum.emplace_back(ALL_TENSORS); // Special tensor number * means all of them
78 else o.tnum.emplace_back(std::stoul(tok[1]));
79 for (size_t i = 2; i < tok.size(); ++i) o.newvals.emplace_back(int(std::stoul(tok[i])));
80 syntax_error = false;
81 } catch (...) { }
82
83 if (syntax_error) LFATAL("Syntax error, expected: transpose(outnum, oldaxisA, oldaxisB, ...), where "
84 "transposed new axis 0 (the outermost dimension, typically batch size) will be "
85 "from oldaxisA, new axis 1 from oldaxisB, etc");
86 }
87 // ----------------------------------------------------------------------------------------------------
88 else if (tok[0] == "order")
89 {
90 if (tok.size() >= 3)
91 try
92 {
93 o.op = Operator::Order;
94 for (size_t i = 1; i < tok.size(); ++i) o.newvals.emplace_back(int(std::stoul(tok[i])));
95 syntax_error = false;
96 } catch (...) { }
97
98 if (syntax_error) LFATAL("Syntax error, expected: order(oldidx0, oldidx1, ...), where the new order will be "
99 "new tensor 0: old tensor oldidx0 (which is zero-based); new tensor 1: "
100 "old tensor oldidx1, etc. It is ok to have duplicated or missing entries.");
101 }
102 // ----------------------------------------------------------------------------------------------------
103 else if (tok[0] == "split")
104 {
105 if (tok.size() >= 4)
106 try
107 {
108 o.op = Operator::Split;
109 if (tok[1] == "*") o.tnum.emplace_back(ALL_TENSORS); // Special tensor number * means all of them
110 else o.tnum.emplace_back(std::stoul(tok[1]));
111 o.tnum.emplace_back(std::stoul(tok[2])); // axis number
112 for (size_t i = 3; i < tok.size(); ++i) o.newvals.emplace_back(int(std::stoul(tok[i])));
113 syntax_error = false;
114 } catch (...) { }
115
116 if (syntax_error) LFATAL("Syntax error, expected: split(outnum, axis, newsize1, ..., newsizeN), where "
117 "axis 0 is the outtermost dimension (typically, batch size), and newsize1 + ... "
118 "+ newsizeN must be equal to the original size of that axis.");
119 }
120 // ----------------------------------------------------------------------------------------------------
121 else if (tok[0] == "merge")
122 {
123 if (tok.size() >= 3)
124 try
125 {
126 o.op = Operator::Merge;
127 o.tnum.emplace_back(std::stoul(tok[1])); // axis number
128 for (size_t i = 2; i < tok.size(); ++i) o.newvals.emplace_back(int(std::stoul(tok[i])));
129 syntax_error = false;
130
131 // Check that tensors to merge are listed in ascending order:
132 for (size_t i = 0; i < o.newvals.size() - 1; ++i)
133 if (o.newvals[i] > o.newvals[i+1]) { syntax_error = true; break; }
134 } catch (...) { }
135
136 if (syntax_error) LFATAL("Syntax error, expected: merge(axis, outnum1, ..., outnumN), where "
137 "axis 0 is the outermost dimension (typically, batch size) and outnum1, ..., "
138 "outnumN are the outputs to merge along that axis. All the outputs to be merged "
139 "must have matching number of dimensions, and matching sizes on all other axes. "
140 "The merged tensor will replace the first output listed in the merge, and the other "
141 "listed will be removed. Outputs to merge must be listed in ascending order (use "
142 "an order() transform first if needed)");
143 }
144 // ----------------------------------------------------------------------------------------------------
145 else LFATAL("Syntax error: Unrecognized operation: " << op);
146
147 itsOps.emplace_back(o);
148 }
149}
150
151// ####################################################################################################
153{
154 // Do not destroy a network that is loading, and do not throw...
155 size_t count = 0;
156 while (itsLoading.load())
157 {
158 std::this_thread::sleep_for(std::chrono::milliseconds(5));
159 try { if (ready()) break; } catch (...) { }
160 if (count++ == 200) { LINFO("Waiting for network load to complete..."); count = 0; }
161 }
162}
163
164// ####################################################################################################
166{
167 // If we are loaded, we are ready to process:
168 if (itsLoaded.load()) return true;
169
170 // If we are loading, check whether loading is complete or threw, otherwise return false as we keep loading:
171 if (itsLoading.load())
172 {
173 if (itsLoadFut.valid() && itsLoadFut.wait_for(std::chrono::milliseconds(2)) == std::future_status::ready)
174 {
175 try { itsLoadFut.get(); itsLoaded.store(true); itsLoading.store(false); LINFO("Network loaded."); return true; }
176 catch (...) { itsLoading.store(false); jevois::warnAndRethrowException(); }
177 }
178 return false;
179 }
180
181 // For Python networks, we need to load in the current thread and block everyone...
182 if (dynamic_cast<jevois::dnn::NetworkPython *>(this) != nullptr)
183 {
184 LINFO("Loading network...");
185 this->load();
186 itsLoaded.store(true);
187 itsLoading.store(false);
188 LINFO("Network loaded.");
189 return true;
190 }
191
192 // Otherwise, trigger an async load:
193 itsLoading.store(true);
194 itsLoadFut = jevois::async(std::bind(&jevois::dnn::Network::load, this));
195 LINFO("Loading network...");
196
197 return false;
198}
199
200// ####################################################################################################
201void jevois::dnn::Network::setExtraInput(size_t num, cv::Mat const & in)
202{
203 // Lock so that we don't change inputs on the network that may be running async:
204 std::lock_guard<std::mutex> _(itsExtraInputsMtx);
205
206 if (in.empty()) itsExtraInputs.erase(num);
207 else itsExtraInputs[num] = in;
208}
209
210// ####################################################################################################
211void jevois::dnn::Network::setExtraInputFromFloat32(size_t num, cv::Mat const & in)
212{
213 size_t const numin = inputShapes().size(); // normal inputs + extra inputs
214 if (num >= numin)
215 LFATAL("Cannot set input " << num << ": network only has " << numin << " inputs");
216
217 std::vector<std::string> extrains = jevois::split(extraintensors::get(), ",\\s*");
218 size_t const numextra = extrains.size();
219 if (numextra > numin)
220 LFATAL(numextra << " extra inputs specified, but net only has " << numin << " total inputs");
221 if (num + numextra < numin)
222 LFATAL("Cannot set input " << num << " (net has " << numin << " inputs, including " << numextra << " extra ones)");
223
224 std::string const & ein = extrains[num + numextra - numin];
225 std::vector<std::string> tok = jevois::split(ein, ":");
226 if (tok.size() != 3) LFATAL("Malformed extra tensor, need <type>:<shape>:external");
227
228 // Decode type and convert the tensor:
229 cv::Mat cvtin;
230 if (tok[0] == "8U") in.convertTo(cvtin, CV_8U);
231 else if (tok[0] == "8S") in.convertTo(cvtin, CV_8S);
232 else if (tok[0] == "16U") in.convertTo(cvtin, CV_16U);
233 else if (tok[0] == "16S") in.convertTo(cvtin, CV_16S);
234 else if (tok[0] == "16F") in.convertTo(cvtin, CV_16F);
235 else if (tok[0] == "32S") in.convertTo(cvtin, CV_32S);
236 else if (tok[0] == "32F") cvtin = in;
237 else if (tok[0] == "64F") in.convertTo(cvtin, CV_64F);
238 else throw std::range_error("Unsupported extra input tensor type [" + tok[0] + "] in " + ein);
239
240 setExtraInput(num, cvtin);
241}
242
243// ####################################################################################################
244std::vector<cv::Mat> jevois::dnn::Network::process(std::vector<cv::Mat> const & blobs,
245 std::vector<std::string> & info)
246{
247 if (ready() == false) LFATAL("Network is not ready");
248 static jevois::TimerOne eitimer("Create extra inputs");
249 static jevois::TimerOne tftimer("Transform outputs");
250
251 std::vector<cv::Mat> outs;
252 std::string const c = comment::get();
253
254 // Add any extra input tensors?
255 std::string const extra = extraintensors::get();
256 if (extra.empty() == false)
257 {
258 // Lock so that we don't change extra inputs on the network that may be running async:
259 std::lock_guard<std::mutex> _(itsExtraInputsMtx);
260
261 eitimer.start();
262
263 std::vector<cv::Mat> newblobs = blobs;
264
265 std::vector<std::string> ins = jevois::split(extra, ",\\s*");
266 for (std::string const & in : ins)
267 {
268 vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
269
270 std::vector<std::string> tok = jevois::split(in, ":");
271 if (tok.size() != 3)
272 LFATAL("Malformed extra tensor, need <type>:<shape>:val1 val2 ... valN (separate multiple tensors by comma)");
273
274 // Decode type and convert to vsi, only those types that OpenCV can support:
275 if (tok[0] == "8U") attr.dtype.vx_type = VSI_NN_TYPE_UINT8;
276 else if (tok[0] == "8S") attr.dtype.vx_type = VSI_NN_TYPE_INT8;
277 else if (tok[0] == "16U") attr.dtype.vx_type = VSI_NN_TYPE_UINT16;
278 else if (tok[0] == "16S") attr.dtype.vx_type = VSI_NN_TYPE_INT16;
279 else if (tok[0] == "16F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT16;
280 else if (tok[0] == "32S") attr.dtype.vx_type = VSI_NN_TYPE_INT32;
281 else if (tok[0] == "32F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT32;
282 else if (tok[0] == "64F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT64;
283 else throw std::range_error("Unsupported extra input tensor type [" + tok[0] + "] in " + extra);
284
285 // Decode the dims:
286 std::vector<size_t> dims = jevois::dnn::strshape(tok[1]);
287 attr.dim_num = dims.size();
288 for (size_t i = 0; i < attr.dim_num; ++i) attr.size[attr.dim_num - 1 - i] = dims[i];
289
290 // Allocate the tensor:
291 attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
292 attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
293 cv::Mat b = jevois::dnn::attrmat(attr);
294
295 // If values are specified as "external", look for the appropriate extra tensor; since that may come from a
296 // post-processor like YOLOjevois, provide an empty tensor if we don't have it yet:
297 if (tok[2] == "external")
298 {
299 auto itr = itsExtraInputs.find(newblobs.size());
300 if (itr == itsExtraInputs.end()) newblobs.emplace_back(std::move(b)); // default blank mat of correct size
301 else newblobs.push_back(itr->second);
302 }
303 else
304 {
305 // Populate the values from the parameter:
306 std::vector<std::string> vals = jevois::split(tok[2], "\\s+");
307 size_t const nvals = vals.size();
308 if (nvals != b.total())
309 LFATAL("Extra in tensor needs " << b.total() << " values, but " << nvals << " given in [" << in << ']');
310 switch (attr.dtype.vx_type)
311 {
312 case VSI_NN_TYPE_UINT8:
313 {
314 uint8_t * ptr = reinterpret_cast<uint8_t *>(b.data);
315 for (std::string const & v : vals) *ptr++ = std::stoi(v);
316 }
317 break;
318
319 case VSI_NN_TYPE_INT8:
320 {
321 int8_t * ptr = reinterpret_cast<int8_t *>(b.data);
322 for (std::string const & v : vals) *ptr++ = std::stoi(v);
323 }
324 break;
325
326 case VSI_NN_TYPE_UINT16:
327 {
328 uint16_t * ptr = reinterpret_cast<uint16_t *>(b.data);
329 for (std::string const & v : vals) *ptr++ = std::stoi(v);
330 }
331 break;
332
333 case VSI_NN_TYPE_INT16:
334 {
335 int16_t * ptr = reinterpret_cast<int16_t *>(b.data);
336 for (std::string const & v : vals) *ptr++ = std::stoi(v);
337 }
338 break;
339
340 case VSI_NN_TYPE_FLOAT16:
341 {
342 cv::hfloat * ptr = reinterpret_cast<cv::hfloat *>(b.data);
343 for (std::string const & v : vals) *ptr++ = cv::hfloat(std::stof(v));
344 }
345 break;
346
347 case VSI_NN_TYPE_INT32:
348 {
349 int32_t * ptr = reinterpret_cast<int32_t *>(b.data);
350 for (std::string const & v : vals) *ptr++ = std::stoi(v);
351 }
352 break;
353
354 case VSI_NN_TYPE_FLOAT32:
355 {
356 float * ptr = reinterpret_cast<float *>(b.data);
357 for (std::string const & v : vals) *ptr++ = std::stof(v);
358 }
359 break;
360
361 case VSI_NN_TYPE_FLOAT64:
362 {
363 double * ptr = reinterpret_cast<double *>(b.data);
364 for (std::string const & v : vals) *ptr++ = std::stod(v);
365 }
366 break;
367
368 default: LFATAL("internal inconsistency");
369 }
370
371 newblobs.emplace_back(std::move(b));
372 }
373 }
374
375 // NOTE: Keep the code below in sync with the default case (no extra inputs). Both branches are duplicated to avoid
376 // having to make a copy of blobs into newblobs in the standard case when we do not have any extra inputs:
377
378 // Show info about input tensors:
379 info.emplace_back("* Input Tensors");
380 for (cv::Mat const & b : newblobs) info.emplace_back("- " + jevois::dnn::shapestr(b));
381 info.emplace_back(eitimer.stop());
382
383 // Run processing on the derived class:
384 info.emplace_back("* Network");
385 if (c.empty() == false) info.emplace_back(c);
386
387 outs = doprocess(newblobs, info);
388 }
389 else
390 {
391 // Show info about input tensors:
392 info.emplace_back("* Input Tensors");
393 for (cv::Mat const & b : blobs) info.emplace_back("- " + jevois::dnn::shapestr(b));
394
395 // Run processing on the derived class:
396 info.emplace_back("* Network");
397 if (c.empty() == false) info.emplace_back(c);
398
399 outs = doprocess(blobs, info);
400 }
401
402 // Show info about output tensors:
403 info.emplace_back("* Output Tensors");
404 for (size_t i = 0; i < outs.size(); ++i) info.emplace_back("- " + jevois::dnn::shapestr(outs[i]));
405
406 // Possibly apply some transformation sequence to the outputs:
407 if (itsOps.empty() == false)
408 {
409 tftimer.start();
410
411 info.emplace_back("* Output Tensors Transforms");
412
413 for (Oper const & o : itsOps)
414 switch(o.op)
415 {
416 // ----------------------------------------------------------------------------------------------------
417 case Operator::Shape:
418 {
419 // tnum guaranteed to have 1 entry; newvals has a valid nD shape.
420 size_t const tnum = o.tnum[0];
421
422 try
423 {
424 outs[tnum] = outs[tnum].reshape(1, o.newvals);
425 info.emplace_back("- shape out " + std::to_string(tnum) + " to " + jevois::dnn::shapestr(outs[tnum]));
426 }
427 catch (...)
428 {
429 LFATAL("While attempting output transform 'shape(" << tnum << ", " <<
430 jevois::dnn::shapestr(o.newvals, outs[tnum].type()) << ")': Cannot reshape from " <<
431 jevois::dnn::shapestr(outs[tnum]) << " to desired dims because of total number of elements mismatch");
432 }
433 }
434 break;
435
436 // ----------------------------------------------------------------------------------------------------
437 case Operator::Transpose:
438 {
439 // tnum guaranteed to have 1 entry; newvals has a list of axis numbers
440 size_t tnum = o.tnum[0];
441
442 // Possibly parallelize if more than one transpose to do:
443 if (tnum == ALL_TENSORS)
444 {
445 std::vector<std::future<void>> fvec;
446 for (size_t t = 0; t < outs.size(); ++t)
447 {
448 info.emplace_back("- transpose out " + std::to_string(t) + " to " + jevois::dnn::shapestr(outs[t]));
449 fvec.emplace_back(jevois::async([&](size_t t)
450 {
451 try
452 {
453 // Do the transpose. cv::transposeND() needs separate source and dest tensors:
454 cv::Mat newout; cv::transposeND(outs[t], o.newvals, newout); outs[t] = std::move(newout);
455 }
456 catch (...)
457 {
458 LFATAL("While attempting output transform 'transpose(" << t << ", " << jevois::join(o.newvals, ", ") <<
459 ")': Cannot transpose from " << jevois::dnn::shapestr(outs[t]) <<
460 " to desired shape, check number of dimensions and that the desired axes contain every "
461 "source axis number exactly once.");
462 }
463 }, t));
464 }
465
466 // Use joinall() to get() all futures and throw a single consolidated exception if any thread threw:
467 jevois::joinall(fvec);
468 }
469 else
470 {
471 // Only one tensor to transpose:
472 try
473 {
474 // Do the transpose. cv::transposeND() needs separate source and dest tensors:
475 cv::Mat newout; cv::transposeND(outs[tnum], o.newvals, newout); outs[tnum] = std::move(newout);
476 info.emplace_back("- transpose out " + std::to_string(tnum) + " to " + jevois::dnn::shapestr(outs[tnum]));
477 }
478 catch (...)
479 {
480 LFATAL("While attempting output transform 'transpose(" << tnum << ", " << jevois::join(o.newvals, ", ") <<
481 ")': Cannot transpose from " << jevois::dnn::shapestr(outs[tnum]) <<
482 " to desired shape, check number of dimensions and that the desired axes contain every source axis "
483 "number exactly once.");
484 }
485 }
486 }
487 break;
488
489 // ----------------------------------------------------------------------------------------------------
490 case Operator::Order:
491 {
492 std::vector<cv::Mat> newouts; int const osiz = int(outs.size());
493
494 for (int idx : o.newvals)
495 if (idx >= 0 && idx < osiz)
496 newouts.push_back(outs[idx]); // no emplace_back() here as one tensor may be pushed several times
497 else
498 LFATAL("While attempting output transform 'order(" << jevois::join(o.newvals, ", ") <<
499 ")': Output number " << idx << " does not exist");
500
501 info.emplace_back("- order: " + jevois::join(o.newvals, ", "));
502 outs = std::move(newouts);
503 }
504 break;
505
506 // ----------------------------------------------------------------------------------------------------
507 case Operator::Split:
508 {
509 size_t const tnum = o.tnum[0];
510 size_t const axis = o.tnum[1];
511
512 std::vector<cv::Mat> newouts;
513 for (size_t i = 0; i < outs.size(); ++i)
514 if (i == tnum || tnum == ALL_TENSORS)
515 {
516 // Split that tensor and push the resulting tensors. split() will check validity of axis, sizes, etc:
517 try
518 {
519 std::vector<cv::Mat> mats = jevois::dnn::split(outs[i], axis, o.newvals);
520
521 // Add those mats, create info string:
522 std::string inf = "- split out " + std::to_string(i) + " to ";
523 for (cv::Mat & m : mats)
524 {
525 inf += jevois::dnn::shapestr(m) + ", ";
526 newouts.emplace_back(m);
527 }
528 info.emplace_back(inf.substr(0, inf.length() - 2));
529 }
530 catch (std::exception const & e)
531 {
532 LFATAL("While attempting output transform 'split(" << i << ", " << axis << ", " <<
533 jevois::join(o.newvals, ", ") << ")': error: " << e.what());
534 }
535 catch (...)
536 {
537 LFATAL("While attempting output transform 'split(" << i << ", " << axis << ", " <<
538 jevois::join(o.newvals, ", ") << ")': unknown error");
539 }
540 }
541 else newouts.push_back(outs[i]); // Just transfer that tensor
542
543 outs = std::move(newouts);
544 }
545 break;
546
547 // ----------------------------------------------------------------------------------------------------
548 case Operator::Merge:
549 {
550 size_t const axis = o.tnum[0]; size_t const numouts = outs.size();
551 std::vector<cv::Mat> newouts;
552
553 // Decide what to do for each output: 0=copy over, 1=run the merge and store result, 2=skip other merge parts.
554 // Also build a vector of those tensors to merge:
555 int action[numouts]; bool didmerge = false; std::vector<cv::Mat> tomerge;
556 for (int i = 0; i < int(numouts); ++i)
557 {
558 if (outs[i].type() != CV_32F && outs[i].type() != CV_64F && outs[i].type() != CV_16F)
559 LFATAL("While attempting output transform 'merge(" << axis << ", " << jevois::join(o.newvals, ", ") <<
560 ")': Cannot merge quantized tensors");
561
562 // Check if i is in our merge list:
563 bool inmerge = false; for (int j : o.newvals) if (i == j) { inmerge = true; break; }
564 if (inmerge)
565 {
566 tomerge.push_back(outs[i]); // no emplace_back() here as we might want to duplicate a tensor
567 if (didmerge == false) { action[i] = 1; didmerge = true; } else action[i] = 2;
568 }
569 else action[i] = 0;
570 }
571
572 // Ready to rock:
573 for (size_t i = 0; i < numouts; ++i)
574 try
575 {
576 switch (action[i])
577 {
578 case 0: // push that tensor unmodified
579 newouts.push_back(outs[i]);
580 break;
581
582 case 1: // push the merged tensor
583 newouts.emplace_back(jevois::dnn::concatenate(tomerge, axis));
584 info.emplace_back("- merged outs " + jevois::join(o.newvals, ", ") + " into " +
585 jevois::dnn::shapestr(newouts.back()) + " (new out " +
586 std::to_string(newouts.size()-1) + ')');
587 break;
588
589 case 2: // skip the other tensors that were merged
590 break;
591
592 default: LFATAL("Internal inconsistency in merge() transform");
593 }
594 }
595 catch (std::exception const & e)
596 {
597 LFATAL("While attempting output transform 'merge(" << axis << ", " <<
598 jevois::join(o.newvals, ", ") << ")': error: " << e.what());
599 }
600 catch (...)
601 {
602 LFATAL("While attempting output transform 'merge(" << axis << ", " <<
603 jevois::join(o.newvals, ", ") << ")': unknown error");
604 }
605
606 outs = std::move(newouts);
607 }
608 break;
609
610 // ----------------------------------------------------------------------------------------------------
611 default:
612 LFATAL("Internal error: Unsupported output transform op " << int(o.op));
613 }
614
615 info.emplace_back(tftimer.stop());
616
617 // Show info about transformed output tensors:
618 info.emplace_back("* Transformed Output Tensors");
619 for (size_t i = 0; i < outs.size(); ++i) info.emplace_back("- " + jevois::dnn::shapestr(outs[i]));
620 }
621
622 return outs;
623}
#define o
Definition Font10x20.C:6
#define ALL_TENSORS
Definition Network.C:25
Simple one-shot timer class.
Definition Timer.H:72
std::string stop(double *seconds)
End a time measurement period, report time spent as: 'prefix: ms (fps)' where % is replaced by values...
Definition Timer.C:162
void start()
Start a time measurement period.
Definition Timer.C:156
Wrapper around an DNN neural network invoked through python.
virtual void load()=0
Load from disk.
bool ready()
Returns true when network is ready to run (loaded and initialized)
Definition Network.C:165
std::vector< cv::Mat > process(std::vector< cv::Mat > const &blobs, std::vector< std::string > &info)
Process input blobs and obtain output blobs.
Definition Network.C:244
void onParamChange(network::outtransform const &param, std::string const &val) override
Definition Network.C:40
void setExtraInput(size_t num, cv::Mat const &in)
Set an extra input tensor, or remove it if given Mat is empty.
Definition Network.C:201
void waitBeforeDestroy()
If network is currently loading, wait until that is done before destroying.
Definition Network.C:152
virtual ~Network()
Destructor.
Definition Network.C:28
void setExtraInputFromFloat32(size_t num, cv::Mat const &in)
Set an extra input tensor from a given F32 tensor, or remove it if given Mat is empty.
Definition Network.C:211
virtual void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
Definition Network.C:32
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:203
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition Log.H:194
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:997
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:512
cv::Mat concatenate(std::vector< cv::Mat > const &tensors, int axis)
Concatenate several tensors into one.
Definition Utils.C:937
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:126
std::vector< size_t > strshape(std::string const &str)
Get a vector of size_t from a string containing AxBxC...
Definition Utils.C:318
std::vector< T > joinall(std::vector< std::future< T > > &fvec, bool multiline=true)
Collect results from several async threads that are all returning a T result.
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
Async execution using a thread pool.
std::string join(std::vector< T > const &tokens, std::string const &delimiter)
Concatenate a vector of tokens into a string.
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