JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
Pipeline.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/Pipeline.H>
19#include <jevois/Debug/Log.H>
20#include <jevois/Util/Utils.H>
21#include <jevois/Util/Async.H>
24#include <jevois/DNN/Utils.H>
25#include <jevois/Core/Engine.H>
26
33
36
45
46#include <opencv2/core/utils/filesystem.hpp>
47
48#include <fstream>
49
50// ####################################################################################################
51
52// Simple class to hold a list of <name, value> pairs for our parameters, with updating the value of existing parameer
53// names if they are set several times (e.g., first set as a global, then set again for a particular network). Note that
54// here we do not check the validity of the parameters. This is delegated to Pipeline::setZooParam():
55namespace
56{
57 class ParHelper
58 {
59 public:
60 // ----------------------------------------------------------------------------------------------------
61 // Set a param from an entry in our yaml file
62 void set(cv::FileNode const & item, std::string const & zf, cv::FileNode const & node)
63 {
64 std::string k = item.name();
65 std::string v;
66 switch (item.type())
67 {
68 case cv::FileNode::INT: v = std::to_string((int)item); break;
69 case cv::FileNode::REAL: v = std::to_string((float)item); break;
70 case cv::FileNode::STRING: v = (std::string)item; break;
71 default:
72 if (&node == &item)
73 LFATAL("Invalid global zoo parameter " << k << " type " << item.type() << " in " << zf);
74 else
75 LFATAL("Invalid zoo parameter " << k << " type " << item.type() << " in " << zf << " node " << node.name());
76 }
77
78 // Update value if param already exists, or add new key,value pair:
79 for (auto & p : params) if (p.first == k) { p.second = v; return; }
80 params.emplace_back(std::make_pair(k, v));
81 }
82
83 // ----------------------------------------------------------------------------------------------------
84 // Get a value for entry subname under item if found, otherwise try our table of globals, otherwise empty
85 std::string pget(cv::FileNode & item, std::string const & subname)
86 {
87 std::string const v = (std::string)item[subname];
88 if (v.empty() == false) return v;
89 for (auto const & p : params) if (p.first == subname) return p.second;
90 return std::string();
91 }
92
93 // ----------------------------------------------------------------------------------------------------
94 // Un-set a previously set global
95 void unset(std::string const & name)
96 {
97 for (auto itr = params.begin(); itr != params.end(); ++itr)
98 if (itr->first == name) { params.erase(itr); return; }
99 }
100
101 // Ordering matters, so use a vector instead of map or unordered_map
102 std::vector<std::pair<std::string /* name */, std::string /* value */>> params;
103 };
104}
105
106// ####################################################################################################
107jevois::dnn::Pipeline::Pipeline(std::string const & instance) :
108 jevois::Component(instance), itsTpre("PreProc"), itsTnet("Network"), itsTpost("PstProc")
109{
110 itsAccelerators["TPU"] = jevois::getNumInstalledTPUs();
111 itsAccelerators["VPU"] = jevois::getNumInstalledVPUs();
112 itsAccelerators["NPU"] = jevois::getNumInstalledNPUs();
113 itsAccelerators["SPU"] = jevois::getNumInstalledSPUs();
114 itsAccelerators["OpenCV"] = 1; // OpenCV always available
115 itsAccelerators["ORT"] = 1; // ONNX runtime always available
116 itsAccelerators["Python"] = 1; // Python always available
117#ifdef JEVOIS_PLATFORM_PRO
118 itsAccelerators["VPUX"] = 1; // VPU emulation on CPU always available through OpenVino
119#endif
120 itsAccelerators["NPUX"] = 1; // NPU over Tim-VX always available since compiled into OpenCV
121
122 LINFO("Detected " <<
123 itsAccelerators["NPU"] << " JeVois-Pro NPUs, " <<
124 itsAccelerators["SPU"] << " Hailo8 SPUs, " <<
125 itsAccelerators["TPU"] << " Coral TPUs, " <<
126 itsAccelerators["VPU"] << " Myriad-X VPUs.");
127}
128
129// ####################################################################################################
131{
132 preproc::freeze(doit);
133 nettype::freeze(doit);
134 postproc::freeze(doit);
135
136 if (itsPreProcessor) itsPreProcessor->freeze(doit);
137 if (itsNetwork) itsNetwork->freeze(doit);
138 if (itsPostProcessor) itsPostProcessor->freeze(doit);
139}
140
141// ####################################################################################################
143{
144 // Freeze all params that users should not modify at runtime:
145 freeze(true);
146}
147
148// ####################################################################################################
150{
151 // If we have a network running async, make sure we wait here until it is done:
152 asyncNetWait();
153}
154
155// ####################################################################################################
157{
158 // Make sure network is not running as we die:
159 asyncNetWait();
160}
161
162// ####################################################################################################
163std::vector<jevois::ObjReco> const & jevois::dnn::Pipeline::latestRecognitions() const
164{
165 if (auto pp = dynamic_cast<jevois::dnn::PostProcessorClassify *>(itsPostProcessor.get()))
166 return pp->latestRecognitions();
167
168 LFATAL("Cannot get recognition results if post-processor is not of type Classify");
169}
170
171// ####################################################################################################
172std::vector<jevois::ObjDetect> const & jevois::dnn::Pipeline::latestDetections() const
173{
174 if (auto pp = dynamic_cast<jevois::dnn::PostProcessorDetect *>(itsPostProcessor.get()))
175 return pp->latestDetections();
176
177 if (auto pp = dynamic_cast<jevois::dnn::PostProcessorPose *>(itsPostProcessor.get()))
178 return pp->latestDetections();
179
180 LFATAL("Cannot get detection results if post-processor is not of type Detect or Pose");
181}
182
183// ####################################################################################################
184std::vector<jevois::ObjDetectOBB> const & jevois::dnn::Pipeline::latestDetectionsOBB() const
185{
186 if (auto pp = dynamic_cast<jevois::dnn::PostProcessorDetectOBB *>(itsPostProcessor.get()))
187 return pp->latestDetectionsOBB();
188
189 LFATAL("Cannot get detection results if post-processor is not of type DetectOBB");
190}
191
192// ####################################################################################################
193std::vector<jevois::PoseSkeleton> const & jevois::dnn::Pipeline::latestSkeletons() const
194{
195 if (auto pp = dynamic_cast<jevois::dnn::PostProcessorPose *>(itsPostProcessor.get()))
196 return pp->latestSkeletons();
197
198 LFATAL("Cannot get pose skeleton results if post-processor is not of type Pose");
199}
200
201// ####################################################################################################
203{
204 // If we were currently doing async processing, wait until network is done:
205 if (itsNetFut.valid())
206 while (true)
207 {
208 if (itsNetFut.wait_for(std::chrono::seconds(5)) == std::future_status::timeout)
209 LERROR("Still waiting for network to finish running...");
210 else break;
211 }
212
213 try { itsNetFut.get(); } catch (...) { }
214 itsOuts.clear();
215}
216
217// ####################################################################################################
218void jevois::dnn::Pipeline::onParamChange(pipeline::filter const &, jevois::dnn::pipeline::Filter const & val)
219{
220 // Reload the zoo file so that the filter can be applied to create the parameter def of pipe, but first we need this
221 // parameter to indeed be updated. So here we just set a flag and the update will occur in process(), after we run the
222 // current model one last time:
223 if (val != filter::get()) itsZooChanged = true;
224}
225
226// ####################################################################################################
227void jevois::dnn::Pipeline::onParamChange(pipeline::zooroot const &, std::string const & val)
228{
229 // Reload the zoo file, but first we need this parameter to indeed be updated. So here we just set a flag and the
230 // update will occur in process(), after we run the current model one last time:
231 if (val.empty() == false && val != zooroot::get()) itsZooChanged = true;
232}
233
234// ####################################################################################################
235void jevois::dnn::Pipeline::onParamChange(pipeline::benchmark const &, bool const & val)
236{
237 if (val)
238 {
239 statsfile::set("benchmark.html");
240 statsfile::freeze(true);
241 }
242 else
243 {
244 statsfile::freeze(false);
245 statsfile::reset();
246 }
247}
248
249// ####################################################################################################
250void jevois::dnn::Pipeline::onParamChange(pipeline::extramodels const &, bool const & val)
251{
252 // Reload the zoo on changed extramodels value:
253 if (val != extramodels::get()) itsZooChanged = true;
254}
255
256// ####################################################################################################
257void jevois::dnn::Pipeline::onParamChange(pipeline::zoo const &, std::string const & val)
258{
259 // Mark the zoo as not changed anymore:
260 itsZooChanged = false;
261
262 // Load zoo file:
263 std::vector<std::string> pipes;
264 scanZoo(jevois::absolutePath(zooroot::get(), val), filter::strget(), pipes, "");
265 LINFO("Found a total of " << pipes.size() << " valid pipelines.");
266
267 // If no pipes found under desired filter, reject:
268 if (pipes.empty()) LFATAL("No pipeline available with zoo file " << val << " and filter " << filter::strget());
269
270 // Update the parameter def of pipe:
271 jevois::ParameterDef<std::string> newdef("pipe", "Pipeline to use, determined by entries in the zoo file and "
272 "by the current filter",
273 pipes[0], pipes, jevois::dnn::pipeline::ParamCateg);
274 pipe::changeParameterDef(newdef);
275
276 // Just changing the def does not change the param value, so change it now:
277 pipe::set(pipes[0]);
278}
279
280// ####################################################################################################
281void jevois::dnn::Pipeline::scanZoo(std::filesystem::path const & zoofile, std::string const & filt,
282 std::vector<std::string> & pipes, std::string const & indent)
283{
284 LINFO(indent << "Scanning model zoo file " << zoofile << " with filter [" << filt << "]...");
285 int ntot = 0, ngood = 0;
286 bool skipextra = (extramodels::get() == false);
287
288 bool has_vpu = false;
289 auto itr = itsAccelerators.find("VPU");
290 if (itr != itsAccelerators.end() && itr->second > 0) has_vpu = true;
291
292 // Scan the zoo file to update the parameter def of the pipe parameter:
293 cv::FileStorage fs(zoofile, cv::FileStorage::READ);
294 if (fs.isOpened() == false) LFATAL("Could not open zoo file " << zoofile);
295 cv::FileNode fn = fs.root();
296 ParHelper ph;
297
298 for (cv::FileNodeIterator fit = fn.begin(); fit != fn.end(); ++fit)
299 {
300 cv::FileNode item = *fit;
301
302 // Process include: directives recursively:
303 if (item.name() == "include")
304 {
305 scanZoo(jevois::absolutePath(zooroot::get(), (std::string)item), filt, pipes, indent + " ");
306 }
307 // Process includedir: directives (only one level of directory is scanned):
308 else if (item.name() == "includedir")
309 {
310 std::filesystem::path const dir = jevois::absolutePath(zooroot::get(), (std::string)item);
311 for (auto const & dent : std::filesystem::recursive_directory_iterator(dir))
312 if (dent.is_regular_file())
313 {
314 std::filesystem::path const path = dent.path();
315 std::filesystem::path const ext = path.extension();
316 if (ext == ".yml" || ext == ".yaml") scanZoo(path, filt, pipes, indent + " ");
317 }
318 }
319 // Unset a previously set global?
320 else if (item.name() == "unset")
321 {
322 ph.unset((std::string)item);
323 }
324 // Set a global:
325 else if (! item.isMap())
326 {
327 ph.set(item, zoofile, item);
328 }
329 // Map type (model definition):
330 else
331 {
332 // Skip this pipe if it is marked 'extramodel' and extramodels is false:
333 if (skipextra)
334 {
335 std::string const isextrastr = ph.pget(item, "extramodel");
336 if (isextrastr.empty() == false)
337 {
338 bool isextra; jevois::paramStringToVal(isextrastr, isextra);
339 if (isextra) continue;
340 }
341 }
342
343 // We have one more model:
344 ++ntot;
345
346 // As a prefix, we use OpenCV for OpenCV models on CPU/OpenCL backends, and VPU for InferenceEngine backend with
347 // Myriad target, VPUX for InferenceEngine/CPU (arm-compute OpenVino plugin, only works on platform), and NPUX for
348 // TimVX/NPU (NPU using TimVX OpenCV extension, uses NPU on platform or emulator on host):
349
350 // Set then get nettype to account for globals:
351 std::string typ = ph.pget(item, "nettype");
352
353 if (typ == "OpenCV")
354 {
355 std::string backend = ph.pget(item, "backend");
356 std::string target = ph.pget(item, "target");
357
358 if (backend == "InferenceEngine")
359 {
360 if (target == "Myriad")
361 {
362 if (has_vpu) typ = "VPU"; // run VPU models on VPU if MyriadX accelerator is present
363#ifdef JEVOIS_PLATFORM_PRO
364 else typ = "VPUX"; // emulate VPU models on CPU through ARM-Compute if MyriadX accelerator not present
365#else
366 else continue; // VPU emulation does not work on host...
367#endif
368 }
369 else if (target == "CPU") typ = "VPUX";
370 }
371 else if (backend == "TimVX" && target == "NPU") typ = "NPUX";
372 }
373
374 // Do not consider a model if we do not have the accelerator for it:
375 bool has_accel = false;
376 itr = itsAccelerators.find(typ);
377 if (itr != itsAccelerators.end() && itr->second > 0) has_accel = true;
378
379 // Add this pipe if it matches our filter and we have the accelerator for it:
380 if ((filt == "All" || typ == filt) && has_accel)
381 {
382 std::string const postproc = ph.pget(item, "postproc");
383 pipes.emplace_back(typ + ':' + postproc + ':' + item.name());
384 ++ngood;
385 }
386 }
387 }
388
389 LINFO(indent << "Found " << ntot << " pipelines, " << ngood << " passed the filter.");
390}
391
392// ####################################################################################################
393void jevois::dnn::Pipeline::onParamChange(pipeline::pipe const &, std::string const & val)
394{
395#ifdef JEVOIS_PRO
396 // Reset the data peekin on each pipe change:
397 itsShowDataPeek = false;
398 itsDataPeekOutIdx = 0;
399 itsDataPeekFreeze = false;
400 itsDataPeekStr.clear();
401#endif
402
403 if (val.empty()) return;
404 itsPipeThrew = false;
405 freeze(false);
406
407 // Clear any errors related to previous pipeline:
408 engine()->clearErrors();
409
410 // Find the desired pipeline, and set it up:
411 std::string const z = jevois::absolutePath(zooroot::get(), zoo::get());
412 std::vector<std::string> tok = jevois::split(val, ":");
413 if (selectPipe(z, tok) == false)
414 LFATAL("Could not find pipeline entry [" << val << "] in zoo file " << z << " and its includes");
415
416 freeze(true);
417}
418
419// ####################################################################################################
420bool jevois::dnn::Pipeline::selectPipe(std::string const & zoofile, std::vector<std::string> const & tok)
421{
422 // We might have frozen processing to Sync if we ran a NetworkPython previously, so unfreeze here:
423 processing::freeze(false);
424 processing::set(jevois::dnn::pipeline::Processing::Async);
425
426 // Check if we have a VPU, to use VPU vs VPUX:
427 bool has_vpu = false;
428 auto itr = itsAccelerators.find("VPU");
429 if (itr != itsAccelerators.end() && itr->second > 0) has_vpu = true;
430 bool vpu_emu = false;
431
432 // Clear any old stats:
433 itsPreStats.clear(); itsNetStats.clear(); itsPstStats.clear();
434 itsStatsWarmup = true; // warmup before computing new stats
435
436 // Open the zoo file:
437 cv::FileStorage fs(zoofile, cv::FileStorage::READ);
438 if (fs.isOpened() == false) LFATAL("Could not open zoo file " << zoofile);
439
440 // Find the desired pipeline:
441 ParHelper ph;
442 cv::FileNode fn = fs.root(), node;
443
444 for (cv::FileNodeIterator fit = fn.begin(); fit != fn.end(); ++fit)
445 {
446 cv::FileNode item = *fit;
447
448 // Process include: directives recursively, end the recursion if we found our pipe in there:
449 if (item.name() == "include")
450 {
451 if (selectPipe(jevois::absolutePath(zooroot::get(), (std::string)item), tok)) return true;
452 }
453
454 // Process includedir: directives (only one level of directory is scanned), end recursion if we found our pipe:
455 else if (item.name() == "includedir")
456 {
457 std::filesystem::path const dir = jevois::absolutePath(zooroot::get(), (std::string)item);
458 for (auto const & dent : std::filesystem::recursive_directory_iterator(dir))
459 if (dent.is_regular_file())
460 {
461 std::filesystem::path const path = dent.path();
462 std::filesystem::path const ext = path.extension();
463 if (ext == ".yml" || ext == ".yaml") if (selectPipe(path, tok)) return true;
464 }
465 }
466
467 // Unset a previously set global?
468 else if (item.name() == "unset")
469 {
470 ph.unset((std::string)item);
471 }
472 // Set a global:
473 else if (! item.isMap())
474 {
475 ph.set(item, zoofile, node);
476 }
477 // This is an entry for a pipeline with a bunch of params under it:
478 else
479 {
480 if (item.name() != tok.back()) continue;
481 if (tok.size() == 1) { node = item; break; }
482 if (tok.size() != 3) LFATAL("Malformed pipeline name: " << jevois::join(tok, ":"));
483
484 // Skip if postproc is no match:
485 std::string postproc = ph.pget(item, "postproc");
486 if (postproc != tok[1] && postproc::strget() != tok[1]) continue;
487
488 std::string nettype = ph.pget(item, "nettype");
489 std::string backend = ph.pget(item, "backend");
490 std::string target = ph.pget(item, "target");
491
492 if (tok[0] == "VPU")
493 {
494 if (nettype == "OpenCV" && backend == "InferenceEngine" && target == "Myriad")
495 { node = item; break; }
496 }
497 else if (tok[0] == "VPUX")
498 {
499 if (nettype == "OpenCV" && backend == "InferenceEngine")
500 {
501 if (target == "Myriad" && has_vpu == false) { vpu_emu = true; node = item; break; }
502 else if (target == "CPU") { node = item; break; }
503 }
504 }
505 else if (tok[0] == "NPUX")
506 {
507 if (nettype == "OpenCV" && backend == "TimVX" && target == "NPU")
508 { node = item; break; }
509 }
510 else
511 {
512 if (nettype == tok[0])
513 { node = item; break; }
514 }
515 }
516 }
517
518 // If the spec was not a match with any entries in the file, return false:
519 if (node.empty()) return false;
520
521 // Found the pipe. First nuke our current pre/net/post:
522 asyncNetWait();
523 itsPreProcessor.reset(); removeSubComponent("preproc", false);
524 itsNetwork.reset(); removeSubComponent("network", false);
525 itsPostProcessor.reset(); removeSubComponent("postproc", false);
526
527 // Then iterate over all pipeline params and set them: first update our table, then set params from the whole table:
528 for (cv::FileNodeIterator fit = node.begin(); fit != node.end(); ++fit)
529 ph.set(*fit, zoofile, node);
530
531 for (auto const & pp : ph.params)
532 {
533 if (vpu_emu && pp.first == "target") setZooParam(pp.first, "CPU", zoofile, node);
534 else setZooParam(pp.first, pp.second, zoofile, node);
535 }
536
537 // Running a python net async segfaults instantly if we are also concurrently running pre or post processing in
538 // python, as python is not re-entrant... so force sync here:
539 if (dynamic_cast<jevois::dnn::NetworkPython *>(itsNetwork.get()) &&
540 (dynamic_cast<jevois::dnn::PreProcessorPython *>(itsPreProcessor.get()) ||
541 dynamic_cast<jevois::dnn::PostProcessorPython *>(itsPostProcessor.get())))
542 {
543 if (processing::get() != jevois::dnn::pipeline::Processing::Sync)
544 {
545 LERROR("Network of type Python cannot run Async if pre- or post- processor are also Python "
546 "-- FORCING Sync processing");
547 processing::set(jevois::dnn::pipeline::Processing::Sync);
548 }
549 processing::freeze(true);
550 }
551
552 return true;
553}
554
555// ####################################################################################################
556void jevois::dnn::Pipeline::setZooParam(std::string const & k, std::string const & v,
557 std::string const & zf, cv::FileNode const & node)
558{
559 // Skip a few reserved YAML-only params:
560 if (k == "extramodel") return; // whether a pipe was marked as 'extramodel' in the zoo
561
562 // The zoo file may contain extra params, like download URL, etc. To ignore those while still catching invalid
563 // values on our parameters, we first check whether the parameter exists, and, if so, try to set it:
564 bool hasparam = false;
565 try { getParamStringUnique(k); hasparam = true; } catch (...) { }
566
567 if (hasparam)
568 {
569 LINFO("Setting ["<<k<<"] to ["<<v<<']');
570
571 try { setParamStringUnique(k, v); }
572 catch (std::exception const & e)
573 { LFATAL("While parsing [" << node.name() << "] in model zoo file " << zf << ": " << e.what()); }
574 catch (...)
575 { LFATAL("While parsing [" << node.name() << "] in model zoo file " << zf << ": unknown error"); }
576 }
577 else if (paramwarn::get())
578 engine()->reportError("WARNING: Unused parameter [" + k + "] in " + zf + " node [" + node.name() + "]");
579}
580
581// ####################################################################################################
582void jevois::dnn::Pipeline::onParamChange(pipeline::preproc const &, pipeline::PreProc const & val)
583{
584 itsPreProcessor.reset(); removeSubComponent("preproc", false);
585
586 switch (val)
587 {
588 case jevois::dnn::pipeline::PreProc::Blob:
589 itsPreProcessor = addSubComponent<jevois::dnn::PreProcessorBlob>("preproc");
590 break;
591
592 case jevois::dnn::pipeline::PreProc::Python:
593 itsPreProcessor = addSubComponent<jevois::dnn::PreProcessorPython>("preproc");
594 break;
595 }
596
597 if (itsPreProcessor) LINFO("Instantiated pre-processor of type " << itsPreProcessor->className());
598 else LINFO("No pre-processor");
599}
600
601// ####################################################################################################
602void jevois::dnn::Pipeline::onParamChange(pipeline::nettype const &, pipeline::NetType const & val)
603{
604 asyncNetWait(); // If currently processing async net, wait until done
605
606 itsNetwork.reset(); removeSubComponent("network", false);
607
608 switch (val)
609 {
610 case jevois::dnn::pipeline::NetType::OpenCV:
611 itsNetwork = addSubComponent<jevois::dnn::NetworkOpenCV>("network");
612 break;
613
614#ifdef JEVOIS_PRO
615
616 case jevois::dnn::pipeline::NetType::ORT:
617 itsNetwork = addSubComponent<jevois::dnn::NetworkONNX>("network");
618 break;
619
620 case jevois::dnn::pipeline::NetType::NPU:
621#ifdef JEVOIS_PLATFORM
622 itsNetwork = addSubComponent<jevois::dnn::NetworkNPU>("network");
623#else // JEVOIS_PLATFORM
624 LFATAL("NPU network is only supported on JeVois-Pro Platform");
625#endif
626 break;
627
628 case jevois::dnn::pipeline::NetType::SPU:
629 itsNetwork = addSubComponent<jevois::dnn::NetworkHailo>("network");
630 break;
631
632 case jevois::dnn::pipeline::NetType::TPU:
633 itsNetwork = addSubComponent<jevois::dnn::NetworkTPU>("network");
634 break;
635#endif
636
637 case jevois::dnn::pipeline::NetType::Python:
638 itsNetwork = addSubComponent<jevois::dnn::NetworkPython>("network");
639 break;
640 }
641
642 if (itsNetwork) LINFO("Instantiated network of type " << itsNetwork->className());
643 else LINFO("No network");
644
645 // We already display a "loading..." message while the network is loading, but some OpenCV networks take a long time
646 // to process their first frame after they are loaded (e.g., YuNet initializes all the anchors on first frame). So
647 // here we set some placeholder text that will appear after the network is loaded and is processing the first frame:
648 itsInputAttrs.clear();
649 itsNetInfo.clear();
650 itsNetInfo.emplace_back("* Input Tensors");
651 itsNetInfo.emplace_back("Initializing network...");
652 itsNetInfo.emplace_back("* Network");
653 itsNetInfo.emplace_back("Initializing network...");
654 itsNetInfo.emplace_back("* Output Tensors");
655 itsNetInfo.emplace_back("Initializing network...");
656 itsAsyncNetInfo = itsNetInfo;
657 itsAsyncNetworkTime = "Network: -";
658 itsAsyncNetworkSecs = 0.0;
659}
660
661// ####################################################################################################
662void jevois::dnn::Pipeline::onParamChange(pipeline::postproc const &, pipeline::PostProc const & val)
663{
664 asyncNetWait(); // If currently processing async net, wait until done
665
666 itsPostProcessor.reset(); removeSubComponent("postproc", false);
667
668 switch (val)
669 {
670 case jevois::dnn::pipeline::PostProc::Classify:
671 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorClassify>("postproc");
672 break;
673 case jevois::dnn::pipeline::PostProc::Detect:
674 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorDetect>("postproc");
675 break;
676 case jevois::dnn::pipeline::PostProc::DetectOBB:
677 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorDetectOBB>("postproc");
678 break;
679 case jevois::dnn::pipeline::PostProc::Segment:
680 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorSegment>("postproc");
681 break;
682 case jevois::dnn::pipeline::PostProc::YuNet:
683 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorYuNet>("postproc");
684 break;
685 case jevois::dnn::pipeline::PostProc::Pose:
686 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorPose>("postproc");
687 break;
688 case jevois::dnn::pipeline::PostProc::Python:
689 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorPython>("postproc");
690 break;
691 case jevois::dnn::pipeline::PostProc::Stub:
692 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorStub>("postproc");
693 break;
694 }
695
696 if (itsPostProcessor) LINFO("Instantiated post-processor of type " << itsPostProcessor->className());
697 else LINFO("No post-processor");
698}
699
700// ####################################################################################################
702{
703 return itsPreProcessor && itsNetwork && itsNetwork->ready() && itsPostProcessor;
704}
705
706// ####################################################################################################
708{
709 if (itsNetFut.valid() && itsNetFut.wait_for(std::chrono::milliseconds(2)) == std::future_status::ready)
710 {
711 itsOuts = itsNetFut.get();
712 itsNetInfo.clear();
713 std::swap(itsNetInfo, itsAsyncNetInfo);
714 itsProcTimes[1] = itsAsyncNetworkTime;
715 itsProcSecs[1] = itsAsyncNetworkSecs;
716 return true;
717 }
718 return false;
719}
720
721// ####################################################################################################
723 jevois::OptGUIhelper * helper, bool idle)
724{
725 // Reload the zoo file if filter has changed:
726 if (itsZooChanged) zoo::set(zoo::get());
727
728 // If the pipeline is throwing exception at any stage, do not do anything here, itsPipeThrew is cleared when selecting
729 // a new pipe:
730 if (itsPipeThrew) return;
731
732 bool const ovl = overlay::get();
733 itsOutImgY = 5; // y text position when using outimg text drawings
734 bool refresh_data_peek = false; // Will be true after each post-processing is actually run
735
736#ifdef JEVOIS_PRO
737 // Open an info window if using GUI and not idle:
738 if (helper && idle == false)
739 {
740 // Set window size applied only on first use ever, otherwise from imgui.ini:
741 ImGui::SetNextWindowPos(ImVec2(24, 159), ImGuiCond_FirstUseEver);
742 ImGui::SetNextWindowSize(ImVec2(464, 877), ImGuiCond_FirstUseEver);
743
744 // Open the window:
745 ImGui::Begin((instanceName() + ':' + getParamStringUnique("pipe")).c_str());
746 }
747#else
748 (void)helper; // avoid compiler warning
749#endif
750
751 // If we want an overlay, show network name on first line:
752 if (ovl)
753 {
754 if (outimg)
755 {
756 jevois::rawimage::writeText(*outimg, instanceName() + ':' + getParamStringUnique("pipe"),
757 5, itsOutImgY, jevois::yuyv::White);
758 itsOutImgY += 11;
759 }
760
761#ifdef JEVOIS_PRO
762 if (helper) helper->itext(instanceName() + ':' + getParamStringUnique("pipe"));
763#endif
764 }
765
766 // If network is not ready, inform user. Be careful that ready() may throw, eg, if bad network name was given and
767 // network could not be loaded:
768 try
769 {
770 if (ready() == false)
771 {
772 char const * msg = itsNetwork ? "Loading network..." : "No network selected...";
773
774 if (outimg)
775 {
776 jevois::rawimage::writeText(*outimg, msg, 5, itsOutImgY, jevois::yuyv::White);
777 itsOutImgY += 11;
778 }
779
780#ifdef JEVOIS_PRO
781 if (helper)
782 {
783 if (idle == false) ImGui::TextUnformatted(msg);
784 if (ovl) helper->itext(msg);
785 }
786#endif
787
788 itsProcTimes = { "PreProc: -", "Network: -", "PstProc: -" };
789 itsProcSecs = { 0.0, 0.0, 0.0 };
790 }
791 else
792 {
793 // Network is ready, run processing, either single-thread (Sync) or threaded (Async):
794 switch (processing::get())
795 {
796 // --------------------------------------------------------------------------------
797 case jevois::dnn::pipeline::Processing::Sync:
798 {
799 asyncNetWait(); // If currently processing async net, wait until done
800
801 // Pre-process:
802 itsTpre.start();
803 if (itsInputAttrs.empty()) itsInputAttrs = itsNetwork->inputShapes();
804 itsBlobs = itsPreProcessor->process(inimg, itsInputAttrs);
805 itsProcTimes[0] = itsTpre.stop(&itsProcSecs[0]);
806 itsPreProcessor->sendreport(mod, outimg, helper, ovl, idle);
807
808 // Network forward pass:
809 itsNetInfo.clear();
810 itsTnet.start();
811 itsOuts = itsNetwork->process(itsBlobs, itsNetInfo);
812 itsProcTimes[1] = itsTnet.stop(&itsProcSecs[1]);
813
814 // Show network info:
815 showInfo(itsNetInfo, mod, outimg, helper, ovl, idle);
816
817 // Post-Processing:
818 itsTpost.start();
819 itsPostProcessor->process(itsOuts, itsPreProcessor.get());
820 itsProcTimes[2] = itsTpost.stop(&itsProcSecs[2]);
821 itsPostProcessor->report(mod, outimg, helper, ovl, idle);
822 refresh_data_peek = true;
823 }
824 break;
825
826 // --------------------------------------------------------------------------------
827 case jevois::dnn::pipeline::Processing::Async:
828 {
829 // We are going to run pre-processing and post-processing synchronously, and network in a thread. One small
830 // complication is that we are going to run post-processing on every frame so that the drawings do not
831 // flicker. We will keep post-processing the same results until new results replace them.
832
833 // Are we running the network, and is it done? If so, get the outputs:
834 bool needpost = checkAsyncNetComplete();
835
836 // If we are not running a network, start it:
837 if (itsNetFut.valid() == false)
838 {
839 // Pre-process in the current thread:
840 itsTpre.start();
841 if (itsInputAttrs.empty()) itsInputAttrs = itsNetwork->inputShapes();
842 itsBlobs = itsPreProcessor->process(inimg, itsInputAttrs);
843 itsProcTimes[0] = itsTpre.stop(&itsProcSecs[0]);
844
845 // Network forward pass in a thread:
846 itsNetFut =
847 jevois::async([this]()
848 {
849 itsTnet.start();
850 std::vector<cv::Mat> outs = itsNetwork->process(itsBlobs, itsAsyncNetInfo);
851 itsAsyncNetworkTime = itsTnet.stop(&itsAsyncNetworkSecs);
852
853 // OpenCV DNN seems to be re-using and overwriting the same output matrices,
854 // so we need to make a deep copy of the outputs if the network type is OpenCV:
855 if (dynamic_cast<jevois::dnn::NetworkOpenCV *>(itsNetwork.get()) == nullptr)
856 return outs;
857
858 std::vector<cv::Mat> outscopy;
859 for (cv::Mat const & m : outs) outscopy.emplace_back(m.clone());
860 return outscopy;
861 });
862 }
863
864 // Report pre-processing results on every frame:
865 itsPreProcessor->sendreport(mod, outimg, helper, ovl, idle);
866
867 // Show network info on every frame:
868 showInfo(itsNetInfo, mod, outimg, helper, ovl, idle);
869
870 // Run post-processing if needed:
871 if (needpost && itsOuts.empty() == false)
872 {
873 itsTpost.start();
874 itsPostProcessor->process(itsOuts, itsPreProcessor.get());
875 itsProcTimes[2] = itsTpost.stop(&itsProcSecs[2]);
876 refresh_data_peek = true;
877 }
878
879 // Report/draw post-processing results on every frame:
880 itsPostProcessor->report(mod, outimg, helper, ovl, idle);
881 }
882 break;
883 }
884
885 // Update our rolling average of total processing time:
886 itsSecsSum += itsProcSecs[0] + itsProcSecs[1] + itsProcSecs[2];
887 if (++itsSecsSumNum == 20) { itsSecsAvg = itsSecsSum / itsSecsSumNum; itsSecsSum = 0.0; itsSecsSumNum = 0; }
888
889 // If computing benchmarking stats, update them now:
890 if (statsfile::get().empty() == false && itsOuts.empty() == false)
891 {
892 static std::vector<std::string> pipelines;
893 static bool statswritten = false;
894 static bool write_separator = false;
895 static size_t benchpipe = 0;
896 size_t constexpr numwarmup = 25;
897 size_t constexpr numbench = 100;
898
899 if (benchmark::get())
900 {
901#ifdef JEVOIS_PRO
902 if (helper && ImGui::Begin("Benchmarking in progress"))
903 {
904 ImGui::TextUnformatted(pipe::strget().c_str());
905 if (itsStatsWarmup) ImGui::Text("Warmup %zu / %zu", itsPreStats.size(), numwarmup);
906 else ImGui::Text("Iteration %zu / %zu", itsPreStats.size(), numbench);
907 ImGui::End();
908 }
909#endif
910
911 if (pipelines.empty())
912 {
913 // User just turned on benchmark mode. List all pipes and start iterating over them:
914 // Valid values string format is List:[A|B|C] where A, B, C are replaced by the actual elements.
915 std::string pipes = pipe::def().validValuesString();
916 size_t const idx = pipes.find('[');
917 pipes = pipes.substr(idx + 1, pipes.length() - idx - 2); // risky code but we control the string's contents
918 pipelines = jevois::split(pipes, "\\|");
919 benchpipe = 0;
920 statswritten = false;
921 pipe::set(pipelines[benchpipe]);
922 processing::freeze(false);
923 processing::set(jevois::dnn::pipeline::Processing::Sync); // run Sync for benchmarking
924 }
925 else
926 {
927 // Switch to the next pipeline after enough stats have been written:
928 if (statswritten)
929 {
930 std::string oldaccel = pipelines[benchpipe].substr(0, pipelines[benchpipe].find(':'));
931 ++benchpipe;
932 statswritten = false;
933 if (benchpipe >= pipelines.size())
934 {
935 pipelines.clear();
936 benchmark::set(false);
937 LINFO("Benchmark complete.");
938 }
939 else
940 {
941 if (oldaccel != pipelines[benchpipe].substr(0, pipelines[benchpipe].find(':'))) write_separator = true;
942 pipe::set(pipelines[benchpipe]);
943 processing::freeze(false);
944 processing::set(jevois::dnn::pipeline::Processing::Sync); // run Sync for benchmarking
945 }
946 }
947 }
948 }
949 else pipelines.clear();
950
951 itsPreStats.push_back(itsProcSecs[0]);
952 itsNetStats.push_back(itsProcSecs[1]);
953 itsPstStats.push_back(itsProcSecs[2]);
954
955 // Discard data for a few warmup frames after we start a new net:
956 if (itsStatsWarmup && itsPreStats.size() == numwarmup)
957 { itsStatsWarmup = false; itsPreStats.clear(); itsNetStats.clear(); itsPstStats.clear(); }
958
959 if (itsPreStats.size() == numbench)
960 {
961 // Compute totals:
962 std::vector<double> tot;
963 for (size_t i = 0; i < itsPreStats.size(); ++i)
964 tot.emplace_back(itsPreStats[i] + itsNetStats[i] + itsPstStats[i]);
965
966 // Append to stats file:
967 std::string const fn = jevois::absolutePath(JEVOIS_SHARE_PATH, statsfile::get());
968 std::ofstream ofs(fn, std::ios_base::app);
969 if (ofs.is_open())
970 {
971 if (write_separator)
972 {
973 ofs << "<tr><td colspan=8></td></tr><tr><td colspan=8></td></tr>" << std::endl;
974 write_separator = false;
975 }
976
977 ofs << "<tr><td class=jvpipe>" << pipe::get() << " </td>";
978
979 std::vector<std::string> insizes;
980 for (cv::Mat const & m : itsBlobs)
981 insizes.emplace_back(jevois::replaceAll(jevois::dnn::shapestr(m), " ", "&nbsp;"));
982 ofs << "<td class=jvnetin>" << jevois::join(insizes, ", ") << "</td>";
983
984 std::vector<std::string> outsizes;
985 for (cv::Mat const & m : itsOuts)
986 outsizes.emplace_back(jevois::replaceAll(jevois::dnn::shapestr(m), " ", "&nbsp;"));
987 ofs << "<td class=jvnetout>" << jevois::join(outsizes, ", ") << "</td>";
988
989 ofs <<
990 "<td class=jvprestats>" << jevois::replaceAll(jevois::secs2str(itsPreStats), " ", "&nbsp;") << "</td>"
991 "<td class=jvnetstats>" << jevois::replaceAll(jevois::secs2str(itsNetStats), " ", "&nbsp;") << "</td>"
992 "<td class=jvpststats>" << jevois::replaceAll(jevois::secs2str(itsPstStats), " ", "&nbsp;") << "</td>"
993 "<td class=jvtotstats>" << jevois::replaceAll(jevois::secs2str(tot), " ", "&nbsp;") << "</td>";
994
995 // Finally report average fps:
996 double avg = 0.0;
997 for (double t : tot) avg += t;
998 avg /= tot.size();
999 if (avg) avg = 1.0 / avg; // from s/frame to frames/s
1000 ofs << "<td class=jvfps>" << std::fixed << std::showpoint << std::setprecision(1) <<
1001 avg << "&nbsp;fps</td></tr>" << std::endl;
1002
1003 // Ready for next round:
1004 itsPreStats.clear();
1005 itsNetStats.clear();
1006 itsPstStats.clear();
1007 LINFO("Network stats appended to " << fn);
1008 statswritten = true;
1009 }
1010 }
1011 }
1012 }
1013 }
1014 catch (...)
1015 {
1016 itsPipeThrew = true;
1017
1018#ifdef JEVOIS_PRO
1019 if (helper) helper->reportAndIgnoreException(instanceName());
1020 else jevois::warnAndIgnoreException(instanceName());
1021#else
1022 jevois::warnAndIgnoreException(instanceName());
1023#endif
1024 }
1025
1026#ifdef JEVOIS_PRO
1027 // Report processing times and close info window if we opened it:
1028 if (helper)
1029 {
1030 std::string total;
1031 if (idle == false || ovl) total = jevois::secs2str(itsSecsAvg);
1032
1033 if (idle == false)
1034 {
1035 // Show processing times:
1036 if (ImGui::CollapsingHeader("Processing Times", ImGuiTreeNodeFlags_DefaultOpen))
1037 {
1038 for (std::string const & s : itsProcTimes) ImGui::TextUnformatted(s.c_str());
1039 ImGui::Text("OVERALL: %s/inference", total.c_str());
1040 }
1041 ImGui::Separator();
1042
1043 // Show a button to allow users to peek output data:
1044 if (ImGui::Button("Peek output data")) itsShowDataPeek = true;
1045
1046 // Done with this window:
1047 ImGui::End();
1048
1049 // Allow user to peek into output data:
1050 showDataPeekWindow(helper, refresh_data_peek);
1051 }
1052
1053 if (ovl)
1054 {
1055 for (std::string const & s : itsProcTimes) helper->itext(s);
1056 helper->itext("OVERALL: " + total + "/inference");
1057 }
1058 }
1059#else
1060 (void)refresh_data_peek; // prevent compiler warning
1061#endif
1062
1063 // Report processing times to outimg if present:
1064 if (outimg && ovl)
1065 {
1066 for (std::string const & s : itsProcTimes)
1067 {
1068 jevois::rawimage::writeText(*outimg, s, 5, itsOutImgY, jevois::yuyv::White);
1069 itsOutImgY += 11;
1070 }
1071 jevois::rawimage::writeText(*outimg, "OVERALL: " + jevois::secs2str(itsSecsAvg) + "/inference",
1072 5, itsOutImgY, jevois::yuyv::White);
1073 itsOutImgY += 11;
1074 }
1075}
1076
1077// ####################################################################################################
1078void jevois::dnn::Pipeline::showInfo(std::vector<std::string> const & info, jevois::StdModule *,
1079 jevois::RawImage * outimg, jevois::OptGUIhelper * helper, bool ovl, bool idle)
1080{
1081 bool show = true;
1082
1083 for (std::string const & s : info)
1084 {
1085 // On JeVois Pro, display info in the GUI:
1086#ifdef JEVOIS_PRO
1087 if (helper && idle == false)
1088 {
1089 // Create collapsible header and get its collapsed status:
1090 if (jevois::stringStartsWith(s, "* "))
1091 show = ImGui::CollapsingHeader(s.c_str() + 2, ImGuiTreeNodeFlags_DefaultOpen);
1092 else if (show)
1093 {
1094 // If header not collapsed, show data:
1095 if (jevois::stringStartsWith(s, "- ")) ImGui::BulletText("%s", s.c_str() + 2);
1096 else ImGui::TextUnformatted(s.c_str());
1097 }
1098 }
1099#else
1100 (void)idle; (void)show; (void)helper; // avoid warning
1101#endif
1102
1103 if (outimg && ovl)
1104 {
1105 jevois::rawimage::writeText(*outimg, s, 5, itsOutImgY, jevois::yuyv::White);
1106 itsOutImgY += 11;
1107 }
1108 }
1109}
1110
1111#ifdef JEVOIS_PRO
1112// ####################################################################################################
1114{
1115 // Do not show anything if user closed the window:
1116 if (itsShowDataPeek == false) return;
1117
1118 // Set window size applied only on first use ever, otherwise from imgui.ini:
1119 ImGui::SetNextWindowPos(ImVec2(100, 50), ImGuiCond_FirstUseEver);
1120 ImGui::SetNextWindowSize(ImVec2(900, 600), ImGuiCond_FirstUseEver);
1121
1122 // Light blue window background:
1123 ImGui::PushStyleColor(ImGuiCol_WindowBg, 0xf0ffe0e0);
1124
1125 // Open the window:
1126 ImGui::Begin("DNN Output Peek", &itsShowDataPeek, ImGuiWindowFlags_HorizontalScrollbar);
1127
1128 // Draw a combo to select which output:
1129 std::vector<std::string> outspecs;
1130 for (size_t i = 0; cv::Mat const & out : itsOuts)
1131 outspecs.emplace_back("Out " + std::to_string(i++) + ": " + jevois::dnn::shapestr(out));
1132 if (helper->combo("##dataPeekOutSelect", outspecs, itsDataPeekOutIdx)) itsDataPeekFreeze = false;
1133
1134 ImGui::SameLine(); ImGui::TextUnformatted(" "); ImGui::SameLine();
1135 helper->toggleButton("Freeze", &itsDataPeekFreeze);
1136 ImGui::Separator();
1137
1138 // Draw the data:
1139 if ( (itsDataPeekFreeze && itsDataPeekStr.empty() == false) || refresh == false)
1140 ImGui::TextUnformatted(itsDataPeekStr.c_str());
1141 else
1142 {
1143 // OpenCV Mat::operator<< cannot handle >2D, try to collapse any dimensions with size 1:
1144 cv::Mat const & out = itsOuts[itsDataPeekOutIdx];
1145 std::vector<int> newsz;
1146 cv::MatSize const & ms = out.size; int const nd = ms.dims();
1147 for (int i = 0; i < nd; ++i) if (ms[i] > 1) newsz.emplace_back(ms[i]);
1148 cv::Mat const out2(newsz, out.type(), out.data);
1149
1150 try
1151 {
1152 std::ostringstream oss;
1153 if (newsz.size() > 3)
1154 throw "too many dims";
1155 else if (newsz.size() == 3)
1156 {
1157 cv::Range ranges[3];
1158 ranges[2] = cv::Range::all();
1159 ranges[1] = cv::Range::all();
1160 for (int i = 0; i < newsz[0]; ++i)
1161 {
1162 oss << "-------------------------------------------------------------------------------\n";
1163 oss << "Third dimension index = " << i << ":\n";
1164 oss << "-------------------------------------------------------------------------------\n\n";
1165 ranges[0] = cv::Range(i, i+1);
1166 cv::Mat slice = out2(ranges); // still 3D but with 1 as 3D dimension...
1167 cv::Mat slice2d(cv::Size(newsz[2], newsz[1]), slice.type(), slice.data); // Now 2D
1168 oss << slice2d << "\n\n";
1169 }
1170 }
1171 else
1172 oss << out2;
1173
1174 itsDataPeekStr = oss.str();
1175 }
1176 catch (...) { itsDataPeekStr = "Sorry, cannot display this type of tensor..."; }
1177
1178 ImGui::TextUnformatted(itsDataPeekStr.c_str());
1179
1180 if (out2.total() > 10000)
1181 {
1182 helper->reportError("Large data peek - Freezing data display\n"
1183 "Click the Freeze button to refresh once");
1184 itsDataPeekFreeze = true;
1185 }
1186 }
1187
1188 // Done with this window:
1189 ImGui::End();
1190 ImGui::PopStyleColor();
1191}
1192#endif
1193
#define JEVOIS_SHARE_PATH
Base path for shared files (e.g., neural network weights, etc)
Definition Config.H:82
A component of a model hierarchy.
Definition Component.H:182
void clearErrors()
Clear all errors currently displayed in the JeVois-Pro GUI.
Definition Engine.C:1410
void reportError(std::string const &err)
Definition Engine.C:1401
Helper class to assist modules in creating graphical and GUI elements.
Definition GUIhelper.H:133
bool combo(std::string const &name, std::vector< std::string > const &items, int &selected_index)
Helper to draw a combobox from a vector of strings.
Definition GUIhelper.C:1710
void reportAndIgnoreException(std::string const &prefix="")
Report current exception in a modal dialog, then ignore it.
Definition GUIhelper.C:2638
void reportError(std::string const &err)
Report an error in an overlay window.
Definition GUIhelper.C:2605
void itext(char const *txt, ImU32 const &col=IM_COL32_BLACK_TRANS, int line=-1)
Draw some overlay text on top of an image.
Definition GUIhelper.C:654
bool toggleButton(char const *name, bool *val)
Helper to draw a toggle button.
Definition GUIhelper.C:2729
A Parameter Definition.
A raw image as coming from a V4L2 Camera and/or being sent out to a USB Gadget.
Definition RawImage.H:111
Base class for a module that supports standardized serial messages.
Definition Module.H:234
Wrapper around an OpenCV DNN neural network.
Wrapper around an DNN neural network invoked through python.
std::vector< ObjDetectOBB > const & latestDetectionsOBB() const
Get the latest oriented bounded box (OBB) detection results, use with caution, not thread-safe.
Definition Pipeline.C:184
bool checkAsyncNetComplete()
Definition Pipeline.C:707
void showInfo(std::vector< std::string > const &info, jevois::StdModule *mod, jevois::RawImage *outimg, jevois::OptGUIhelper *helper, bool ovl, bool idle)
Definition Pipeline.C:1078
void preUninit() override
Called before all sub-Components are uninit()ed.
Definition Pipeline.C:149
void showDataPeekWindow(jevois::GUIhelper *helper, bool refresh)
Definition Pipeline.C:1113
std::vector< ObjReco > const & latestRecognitions() const
Get the latest recognition results, use with caution, not thread-safe.
Definition Pipeline.C:163
void process(jevois::RawImage const &inimg, jevois::StdModule *mod, jevois::RawImage *outimg, jevois::OptGUIhelper *helper, bool idle=false)
Process an input image, send results to serial/image/gui.
Definition Pipeline.C:722
void onParamChange(pipeline::zooroot const &param, std::string const &val) override
Definition Pipeline.C:227
virtual ~Pipeline()
Destructor.
Definition Pipeline.C:156
bool ready() const
Returns true when all three of preproc, net, and postproc are ready.
Definition Pipeline.C:701
Pipeline(std::string const &instance)
Constructor.
Definition Pipeline.C:107
void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
Definition Pipeline.C:130
std::vector< PoseSkeleton > const & latestSkeletons() const
Get the latest skeletons, use with caution, not thread-safe.
Definition Pipeline.C:193
std::vector< ObjDetect > const & latestDetections() const
Get the latest detection results, use with caution, not thread-safe.
Definition Pipeline.C:172
void postInit() override
Called after all sub-Components are init()ed.
Definition Pipeline.C:142
Post-Processor for neural network pipeline.
Post-Processor for neural network pipeline for oriented bounding box (OBB) object detection.
Post-Processor for neural network pipeline.
Post-Processor for neural network pipeline, for human/animal/other pose detection (skeleton)
Post-Processor for neural network pipeline.
Pre-Processor for neural network pipeline written in python.
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
std::string warnAndIgnoreException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition Log.C:236
#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
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
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
Write some text in an image.
void paramStringToVal(std::string const &valstring, T &result)
Machine-readable conversion from string to T, for use in jevois::Parameter.
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 secs2str(double secs)
Report a duration given in seconds with variable units (ns, us, ms, or s), with precision of 2 decima...
Definition Utils.C:479
std::string join(std::vector< T > const &tokens, std::string const &delimiter)
Concatenate a vector of tokens into a string.
std::filesystem::path absolutePath(std::filesystem::path const &root, std::filesystem::path const &path)
Compute an absolute path from two paths.
Definition Utils.C:386
bool stringStartsWith(std::string const &str, std::string const &prefix)
Return true if str starts with prefix (including if both strings are equal)
Definition Utils.C:295
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
std::string replaceAll(std::string const &str, std::string const &from, std::string const &to)
Replace all instances of 'from' with 'to'.
Definition Utils.C:362
unsigned short constexpr White
YUYV color value.
Definition RawImage.H:59
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2
size_t getNumInstalledVPUs()
Get the number of Myriad-X VPUs present on this system.
Definition SysInfo.C:112
size_t getNumInstalledNPUs()
Get the number of JeVois-Pro NPUs present on this system.
Definition SysInfo.C:127
size_t getNumInstalledTPUs()
Get the number of Coral TPUs present on this system.
Definition SysInfo.C:87
size_t getNumInstalledSPUs()
Get the number of Hailo8 SPUs present on this system.
Definition SysInfo.C:138