44 #include <opencv2/core/utils/filesystem.hpp>
60 void set(cv::FileNode
const & item, std::string
const & zf, cv::FileNode
const & node)
62 std::string k = item.name();
68 case cv::FileNode::STRING: v = (std::string)item;
break;
71 LFATAL(
"Invalid global zoo parameter " << k <<
" type " << item.type() <<
" in " << zf);
73 LFATAL(
"Invalid zoo parameter " << k <<
" type " << item.type() <<
" in " << zf <<
" node " << node.name());
77 for (
auto & p : params)
if (p.first == k) { p.second = v;
return; }
78 params.emplace_back(std::make_pair(k, v));
83 std::string pget(cv::FileNode & item, std::string
const & subname)
85 std::string
const v = (std::string)item[subname];
86 if (v.empty() ==
false)
return v;
87 for (
auto const & p : params)
if (p.first == subname)
return p.second;
93 void unset(std::string
const & name)
95 for (
auto itr = params.begin(); itr != params.end(); ++itr)
96 if (itr->first == name) { params.erase(itr);
return; }
100 std::vector<std::pair<std::string , std::string >> params;
106 jevois::
Component(instance), itsTpre(
"PreProc"), itsTnet(
"Network"), itsTpost(
"PstProc")
112 itsAccelerators[
"OpenCV"] = 1;
113 itsAccelerators[
"ORT"] = 1;
114 itsAccelerators[
"Python"] = 1;
115 #ifdef JEVOIS_PLATFORM_PRO
116 itsAccelerators[
"VPUX"] = 1;
118 itsAccelerators[
"NPUX"] = 1;
121 itsAccelerators[
"NPU"] <<
" JeVois-Pro NPUs, " <<
122 itsAccelerators[
"SPU"] <<
" Hailo8 SPUs, " <<
123 itsAccelerators[
"TPU"] <<
" Coral TPUs, " <<
124 itsAccelerators[
"VPU"] <<
" Myriad-X VPUs.");
130 preproc::freeze(doit);
131 nettype::freeze(doit);
132 postproc::freeze(doit);
134 if (itsPreProcessor) itsPreProcessor->freeze(doit);
135 if (itsNetwork) itsNetwork->freeze(doit);
136 if (itsPostProcessor) itsPostProcessor->freeze(doit);
161 void jevois::dnn::Pipeline::asyncNetWait()
164 if (itsNetFut.valid())
167 if (itsNetFut.wait_for(std::chrono::seconds(5)) == std::future_status::timeout)
168 LERROR(
"Still waiting for network to finish running...");
172 try { itsNetFut.get(); }
catch (...) { }
177 void jevois::dnn::Pipeline::onParamChange(pipeline::filter
const & JEVOIS_UNUSED_PARAM(param),
178 jevois::dnn::pipeline::Filter
const & JEVOIS_UNUSED_PARAM(val))
183 itsZooChanged =
true;
187 void jevois::dnn::Pipeline::onParamChange(pipeline::zooroot
const & JEVOIS_UNUSED_PARAM(param),
188 std::string
const & JEVOIS_UNUSED_PARAM(val))
192 itsZooChanged =
true;
196 void jevois::dnn::Pipeline::onParamChange(pipeline::benchmark
const & JEVOIS_UNUSED_PARAM(param),
201 statsfile::set(
"benchmark.html");
202 statsfile::freeze(
true);
206 statsfile::freeze(
false);
212 void jevois::dnn::Pipeline::onParamChange(pipeline::zoo
const & JEVOIS_UNUSED_PARAM(param),
213 std::string
const & val)
216 itsPreProcessor.reset();
218 itsPostProcessor.reset();
222 std::vector<std::string> pipes;
224 LINFO(
"Found a total of " << pipes.size() <<
" valid pipelines.");
227 if (pipes.empty() ==
false)
230 "by the current filter",
231 pipes[0], pipes, jevois::dnn::pipeline::ParamCateg);
232 pipe::changeParameterDef(newdef);
240 void jevois::dnn::Pipeline::scanZoo(std::filesystem::path
const & zoofile, std::string
const & filt,
241 std::vector<std::string> & pipes, std::string
const & indent)
243 LINFO(indent <<
"Scanning model zoo file " << zoofile <<
" with filter [" << filt <<
"]...");
244 int ntot = 0, ngood = 0;
246 bool has_vpu =
false;
247 auto itr = itsAccelerators.find(
"VPU");
248 if (itr != itsAccelerators.end() && itr->second > 0) has_vpu =
true;
251 cv::FileStorage fs(zoofile, cv::FileStorage::READ);
252 if (fs.isOpened() ==
false)
LFATAL(
"Could not open zoo file " << zoofile);
253 cv::FileNode fn = fs.root();
256 for (cv::FileNodeIterator fit = fn.begin(); fit != fn.end(); ++fit)
258 cv::FileNode item = *fit;
261 if (item.name() ==
"include")
266 else if (item.name() ==
"includedir")
269 for (
auto const & dent : std::filesystem::recursive_directory_iterator(dir))
270 if (dent.is_regular_file())
272 std::filesystem::path
const path = dent.path();
273 std::filesystem::path
const ext = path.extension();
274 if (ext ==
".yml" || ext ==
".yaml") scanZoo(path, filt, pipes, indent +
" ");
278 else if (item.name() ==
"unset")
280 ph.unset((std::string)item);
283 else if (! item.isMap())
285 ph.set(item, zoofile, item);
297 std::string typ = ph.pget(item,
"nettype");
301 std::string backend = ph.pget(item,
"backend");
302 std::string target = ph.pget(item,
"target");
304 if (backend ==
"InferenceEngine")
306 if (target ==
"Myriad")
308 if (has_vpu) typ =
"VPU";
309 #ifdef JEVOIS_PLATFORM_PRO
315 else if (target ==
"CPU") typ =
"VPUX";
317 else if (backend ==
"TimVX" && target ==
"NPU") typ =
"NPUX";
321 bool has_accel =
false;
322 itr = itsAccelerators.find(typ);
323 if (itr != itsAccelerators.end() && itr->second > 0) has_accel =
true;
326 if ((filt ==
"All" || typ == filt) && has_accel)
328 std::string
const postproc = ph.pget(item,
"postproc");
329 pipes.emplace_back(typ +
':' + postproc +
':' + item.name());
335 LINFO(indent <<
"Found " << ntot <<
" pipelines, " << ngood <<
" passed the filter.");
339 void jevois::dnn::Pipeline::onParamChange(pipeline::pipe
const & JEVOIS_UNUSED_PARAM(param), std::string
const & val)
341 if (val.empty())
return;
342 itsPipeThrew =
false;
346 engine()->clearErrors();
351 if (selectPipe(z, tok) ==
false)
352 LFATAL(
"Could not find pipeline entry [" << val <<
"] in zoo file " << z <<
" and its includes");
358 bool jevois::dnn::Pipeline::selectPipe(std::string
const & zoofile, std::vector<std::string>
const & tok)
361 processing::freeze(
false);
362 processing::set(jevois::dnn::pipeline::Processing::Async);
365 bool has_vpu =
false;
366 auto itr = itsAccelerators.find(
"VPU");
367 if (itr != itsAccelerators.end() && itr->second > 0) has_vpu =
true;
368 bool vpu_emu =
false;
371 itsPreStats.clear(); itsNetStats.clear(); itsPstStats.clear();
372 itsStatsWarmup =
true;
375 cv::FileStorage fs(zoofile, cv::FileStorage::READ);
376 if (fs.isOpened() ==
false)
LFATAL(
"Could not open zoo file " << zoofile);
380 cv::FileNode fn = fs.root(), node;
382 for (cv::FileNodeIterator fit = fn.begin(); fit != fn.end(); ++fit)
384 cv::FileNode item = *fit;
387 if (item.name() ==
"include")
393 else if (item.name() ==
"includedir")
396 for (
auto const & dent : std::filesystem::recursive_directory_iterator(dir))
397 if (dent.is_regular_file())
399 std::filesystem::path
const path = dent.path();
400 std::filesystem::path
const ext = path.extension();
401 if (ext ==
".yml" || ext ==
".yaml")
if (selectPipe(path, tok))
return true;
406 else if (item.name() ==
"unset")
408 ph.unset((std::string)item);
411 else if (! item.isMap())
413 ph.set(item, zoofile, node);
418 if (item.name() != tok.back())
continue;
419 if (tok.size() == 1) { node = item;
break; }
423 std::string postproc = ph.pget(item,
"postproc");
424 if (postproc != tok[1] && postproc::strget() != tok[1])
continue;
426 std::string nettype = ph.pget(item,
"nettype");
427 std::string backend = ph.pget(item,
"backend");
428 std::string target = ph.pget(item,
"target");
432 if (nettype ==
"OpenCV" && backend ==
"InferenceEngine" && target ==
"Myriad")
433 { node = item;
break; }
435 else if (tok[0] ==
"VPUX")
437 if (nettype ==
"OpenCV" && backend ==
"InferenceEngine")
439 if (target ==
"Myriad" && has_vpu ==
false) { vpu_emu =
true; node = item;
break; }
440 else if (target ==
"CPU") { node = item;
break; }
443 else if (tok[0] ==
"NPUX")
445 if (nettype ==
"OpenCV" && backend ==
"TimVX" && target ==
"NPU")
446 { node = item;
break; }
450 if (nettype == tok[0])
451 { node = item;
break; }
457 if (node.empty())
return false;
461 itsPreProcessor.reset(); removeSubComponent(
"preproc",
false);
462 itsNetwork.reset(); removeSubComponent(
"network",
false);
463 itsPostProcessor.reset(); removeSubComponent(
"postproc",
false);
470 for (cv::FileNodeIterator fit = node.begin(); fit != node.end(); ++fit)
471 ph.set(*fit, zoofile, node);
473 for (
auto const & pp : ph.params)
475 if (vpu_emu && pp.first ==
"target") setZooParam(pp.first,
"CPU", zoofile, node);
476 else setZooParam(pp.first, pp.second, zoofile, node);
487 LERROR(
"Network of type Python cannot run Async if pre- or post- processor are also Python "
488 "-- FORCING Sync processing");
489 processing::set(jevois::dnn::pipeline::Processing::Sync);
491 processing::freeze(
true);
498 void jevois::dnn::Pipeline::setZooParam(std::string
const & k, std::string
const & v,
499 std::string
const & zf, cv::FileNode
const & node)
503 bool hasparam =
false;
504 try { getParamStringUnique(k); hasparam =
true; }
catch (...) { }
508 LINFO(
"Setting ["<<k<<
"] to ["<<v<<
']');
510 try { setParamStringUnique(k, v); }
511 catch (std::exception
const & e)
512 {
LFATAL(
"While parsing [" << node.name() <<
"] in model zoo file " << zf <<
": " << e.what()); }
514 {
LFATAL(
"While parsing [" << node.name() <<
"] in model zoo file " << zf <<
": unknown error"); }
517 engine()->reportError(
"WARNING: Unused parameter [" + k +
"] in " + zf +
" node [" + node.name() +
"]");
521 void jevois::dnn::Pipeline::onParamChange(pipeline::preproc
const & JEVOIS_UNUSED_PARAM(param),
522 pipeline::PreProc
const & val)
524 itsPreProcessor.reset(); removeSubComponent(
"preproc",
false);
528 case jevois::dnn::pipeline::PreProc::Blob:
529 itsPreProcessor = addSubComponent<jevois::dnn::PreProcessorBlob>(
"preproc");
532 case jevois::dnn::pipeline::PreProc::Python:
533 itsPreProcessor = addSubComponent<jevois::dnn::PreProcessorPython>(
"preproc");
536 case jevois::dnn::pipeline::PreProc::Custom:
541 if (itsPreProcessor)
LINFO(
"Instantiated pre-processor of type " << itsPreProcessor->className());
542 else LINFO(
"No pre-processor");
548 itsPreProcessor.reset(); removeSubComponent(
"preproc",
false);
549 itsPreProcessor = pp;
550 preproc::set(jevois::dnn::pipeline::PreProc::Custom);
552 if (itsPreProcessor)
LINFO(
"Attached pre-processor of type " << itsPreProcessor->className());
553 else LINFO(
"No pre-processor");
557 void jevois::dnn::Pipeline::onParamChange(pipeline::nettype
const & JEVOIS_UNUSED_PARAM(param),
558 pipeline::NetType
const & val)
562 itsNetwork.reset(); removeSubComponent(
"network",
false);
566 case jevois::dnn::pipeline::NetType::OpenCV:
567 itsNetwork = addSubComponent<jevois::dnn::NetworkOpenCV>(
"network");
572 case jevois::dnn::pipeline::NetType::ORT:
573 itsNetwork = addSubComponent<jevois::dnn::NetworkONNX>(
"network");
576 case jevois::dnn::pipeline::NetType::NPU:
577 #ifdef JEVOIS_PLATFORM
578 itsNetwork = addSubComponent<jevois::dnn::NetworkNPU>(
"network");
579 #else // JEVOIS_PLATFORM
580 LFATAL(
"NPU network is only supported on JeVois-Pro Platform");
584 case jevois::dnn::pipeline::NetType::SPU:
585 itsNetwork = addSubComponent<jevois::dnn::NetworkHailo>(
"network");
588 case jevois::dnn::pipeline::NetType::TPU:
589 itsNetwork = addSubComponent<jevois::dnn::NetworkTPU>(
"network");
593 case jevois::dnn::pipeline::NetType::Python:
594 itsNetwork = addSubComponent<jevois::dnn::NetworkPython>(
"network");
597 case jevois::dnn::pipeline::NetType::Custom:
602 if (itsNetwork)
LINFO(
"Instantiated network of type " << itsNetwork->className());
603 else LINFO(
"No network");
608 itsInputAttrs.clear();
610 itsNetInfo.emplace_back(
"* Input Tensors");
611 itsNetInfo.emplace_back(
"Initializing network...");
612 itsNetInfo.emplace_back(
"* Network");
613 itsNetInfo.emplace_back(
"Initializing network...");
614 itsNetInfo.emplace_back(
"* Output Tensors");
615 itsNetInfo.emplace_back(
"Initializing network...");
616 itsAsyncNetInfo = itsNetInfo;
617 itsAsyncNetworkTime =
"Network: -";
618 itsAsyncNetworkSecs = 0.0;
625 itsNetwork.reset(); removeSubComponent(
"network",
false);
627 nettype::set(jevois::dnn::pipeline::NetType::Custom);
629 if (itsNetwork)
LINFO(
"Attached network of type " << itsNetwork->className());
630 else LINFO(
"No network");
632 itsInputAttrs.clear();
636 void jevois::dnn::Pipeline::onParamChange(pipeline::postproc
const & JEVOIS_UNUSED_PARAM(param),
637 pipeline::PostProc
const & val)
641 itsPostProcessor.reset(); removeSubComponent(
"postproc",
false);
645 case jevois::dnn::pipeline::PostProc::Classify:
646 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorClassify>(
"postproc");
648 case jevois::dnn::pipeline::PostProc::Detect:
649 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorDetect>(
"postproc");
651 case jevois::dnn::pipeline::PostProc::Segment:
652 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorSegment>(
"postproc");
654 case jevois::dnn::pipeline::PostProc::YuNet:
655 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorYuNet>(
"postproc");
657 case jevois::dnn::pipeline::PostProc::Python:
658 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorPython>(
"postproc");
660 case jevois::dnn::pipeline::PostProc::Stub:
661 itsPostProcessor = addSubComponent<jevois::dnn::PostProcessorStub>(
"postproc");
662 case jevois::dnn::pipeline::PostProc::Custom:
667 if (itsPostProcessor)
LINFO(
"Instantiated post-processor of type " << itsPostProcessor->className());
668 else LINFO(
"No post-processor");
675 itsPostProcessor.reset(); removeSubComponent(
"postproc",
false);
676 itsPostProcessor = pp;
677 postproc::set(jevois::dnn::pipeline::PostProc::Custom);
679 if (itsPostProcessor)
LINFO(
"Attached post-processor of type " << itsPostProcessor->className());
680 else LINFO(
"No post-processor");
686 return itsPreProcessor && itsNetwork && itsNetwork->ready() && itsPostProcessor;
690 bool jevois::dnn::Pipeline::checkAsyncNetComplete()
692 if (itsNetFut.valid() && itsNetFut.wait_for(std::chrono::milliseconds(2)) == std::future_status::ready)
694 itsOuts = itsNetFut.get();
696 std::swap(itsNetInfo, itsAsyncNetInfo);
697 itsProcTimes[1] = itsAsyncNetworkTime;
698 itsProcSecs[1] = itsAsyncNetworkSecs;
709 if (itsZooChanged) { itsZooChanged =
false; zoo::set(
zoo::get()); }
713 if (itsPipeThrew)
return;
720 if (helper && idle ==
false) ImGui::Begin((instanceName() +
':' + getParamStringUnique(
"pipe")).c_str());
731 5, itsOutImgY, jevois::yuyv::White);
736 if (helper) helper->
itext(instanceName() +
':' + getParamStringUnique(
"pipe"));
744 if (ready() ==
false)
746 char const * msg = itsNetwork ?
"Loading network..." :
"No network selected...";
757 if (idle ==
false) ImGui::TextUnformatted(msg);
758 if (ovl) helper->
itext(msg);
762 itsProcTimes = {
"PreProc: -",
"Network: -",
"PstProc: -" };
763 itsProcSecs = { 0.0, 0.0, 0.0 };
771 case jevois::dnn::pipeline::Processing::Sync:
777 if (itsInputAttrs.empty()) itsInputAttrs = itsNetwork->inputShapes();
778 itsBlobs = itsPreProcessor->process(inimg, itsInputAttrs);
779 itsProcTimes[0] = itsTpre.stop(&itsProcSecs[0]);
780 itsPreProcessor->sendreport(mod, outimg, helper, ovl, idle);
785 itsOuts = itsNetwork->process(itsBlobs, itsNetInfo);
786 itsProcTimes[1] = itsTnet.stop(&itsProcSecs[1]);
789 showInfo(itsNetInfo, mod, outimg, helper, ovl, idle);
793 itsPostProcessor->process(itsOuts, itsPreProcessor.get());
794 itsProcTimes[2] = itsTpost.stop(&itsProcSecs[2]);
795 itsPostProcessor->report(mod, outimg, helper, ovl, idle);
800 case jevois::dnn::pipeline::Processing::Async:
807 bool needpost = checkAsyncNetComplete();
810 if (itsNetFut.valid() ==
false)
814 if (itsInputAttrs.empty()) itsInputAttrs = itsNetwork->inputShapes();
815 itsBlobs = itsPreProcessor->process(inimg, itsInputAttrs);
816 itsProcTimes[0] = itsTpre.stop(&itsProcSecs[0]);
823 std::vector<cv::Mat> outs = itsNetwork->process(itsBlobs, itsAsyncNetInfo);
824 itsAsyncNetworkTime = itsTnet.stop(&itsAsyncNetworkSecs);
831 std::vector<cv::Mat> outscopy;
832 for (cv::Mat
const & m : outs) outscopy.emplace_back(m.clone());
838 itsPreProcessor->sendreport(mod, outimg, helper, ovl, idle);
841 showInfo(itsNetInfo, mod, outimg, helper, ovl, idle);
844 if (needpost && itsOuts.empty() ==
false)
847 itsPostProcessor->process(itsOuts, itsPreProcessor.get());
848 itsProcTimes[2] = itsTpost.stop(&itsProcSecs[2]);
852 itsPostProcessor->report(mod, outimg, helper, ovl, idle);
858 itsSecsSum += itsProcSecs[0] + itsProcSecs[1] + itsProcSecs[2];
859 if (++itsSecsSumNum == 20) { itsSecsAvg = itsSecsSum / itsSecsSumNum; itsSecsSum = 0.0; itsSecsSumNum = 0; }
862 if (
statsfile::get().empty() ==
false && itsOuts.empty() ==
false)
864 static std::vector<std::string> pipelines;
865 static bool statswritten =
false;
866 static size_t benchpipe = 0;
870 if (pipelines.empty())
874 std::string pipes = pipe::def().validValuesString();
875 size_t const idx = pipes.find(
'[');
876 pipes = pipes.substr(idx + 1, pipes.length() - idx - 2);
879 statswritten =
false;
880 pipe::set(pipelines[benchpipe]);
885 helper->
reportError(
"Benchmarking: " +pipelines[benchpipe]);
895 statswritten =
false;
896 if (benchpipe >= pipelines.size())
899 benchmark::set(
false);
901 if (helper) helper->
reportError(
"DNN benchmark complete.");
906 pipe::set(pipelines[benchpipe]);
908 if (helper) helper->
reportError(
"Benchmarking: " +pipelines[benchpipe]);
914 else pipelines.clear();
916 itsPreStats.push_back(itsProcSecs[0]);
917 itsNetStats.push_back(itsProcSecs[1]);
918 itsPstStats.push_back(itsProcSecs[2]);
921 if (itsStatsWarmup && itsPreStats.size() == 200)
922 { itsStatsWarmup =
false; itsPreStats.clear(); itsNetStats.clear(); itsPstStats.clear(); }
924 if (itsPreStats.size() == 500)
927 std::vector<double> tot;
928 for (
size_t i = 0; i < itsPreStats.size(); ++i)
929 tot.emplace_back(itsPreStats[i] + itsNetStats[i] + itsPstStats[i]);
933 std::ofstream ofs(fn, std::ios_base::app);
936 ofs <<
"<tr><td class=jvpipe>" <<
pipe::get() <<
" </td>";
938 std::vector<std::string> insizes;
939 for (cv::Mat
const & m : itsBlobs)
941 ofs <<
"<td class=jvnetin>" <<
jevois::join(insizes,
", ") <<
"</td>";
943 std::vector<std::string> outsizes;
944 for (cv::Mat
const & m : itsOuts)
946 ofs <<
"<td class=jvnetout>" <<
jevois::join(outsizes,
", ") <<
"</td>";
956 for (
double t : tot) avg += t;
958 if (avg) avg = 1.0 / avg;
959 ofs <<
"<td class=jvfps>" << std::fixed << std::showpoint << std::setprecision(1) <<
960 avg <<
" fps</td></tr>" << std::endl;
966 LINFO(
"Network stats appended to " << fn);
994 if (ImGui::CollapsingHeader(
"Processing Times", ImGuiTreeNodeFlags_DefaultOpen))
996 for (std::string
const & s : itsProcTimes) ImGui::TextUnformatted(s.c_str());
997 ImGui::Text(
"OVERALL: %s/inference", total.c_str());
1004 for (std::string
const & s : itsProcTimes) helper->
itext(s);
1005 helper->
itext(
"OVERALL: " + total +
"/inference");
1013 for (std::string
const & s : itsProcTimes)
1019 5, itsOutImgY, jevois::yuyv::White);
1025 void jevois::dnn::Pipeline::showInfo(std::vector<std::string>
const & info,
1032 for (std::string
const & s : info)
1036 if (helper && idle ==
false)
1040 show = ImGui::CollapsingHeader(s.c_str() + 2, ImGuiTreeNodeFlags_DefaultOpen);
1045 else ImGui::TextUnformatted(s.c_str());
1049 (void)idle; (void)show; (void)helper;