18#ifdef JEVOIS_PLATFORM_PRO
25#include <vsi_nn_version.h>
28#define VNN_APP_DEBUG (FALSE)
34#define NEW_VXNODE(_node, _type, _in, _out, _uid) do { \
35 _node = vsi_nn_AddNode(itsGraph, _type, _in, _out, NULL); \
36 _node->uid = (uint32_t)_uid; \
37 if (NULL == _node) LFATAL("NEW_VXNODE failed"); \
40#define NEW_VIRTUAL_TENSOR(_id, _attr, _dtype) do { \
41 memset(_attr.size, 0, VSI_NN_MAX_DIM_NUM * sizeof(vsi_size_t)); \
42 _attr.dim_num = VSI_NN_DIM_AUTO; \
43 _attr.vtl = !VNN_APP_DEBUG; \
44 _attr.is_const = FALSE; \
45 _attr.dtype.vx_type = _dtype; \
46 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
47 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_VIRTUAL_TENSOR failed"); \
51#define NEW_CONST_TENSOR(_id, _attr, _dtype, _ofst, _size) do { \
52 data = load_data(fp, _ofst, _size); \
54 _attr.is_const = TRUE; \
55 _attr.dtype.vx_type = _dtype; \
56 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, data); \
58 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_CONST_TENSOR failed"); \
62#define NEW_NORM_TENSOR(_id, _attr, _dtype) do { \
64 _attr.is_const = FALSE; \
65 _attr.dtype.vx_type = _dtype; \
66 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
67 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_NORM_TENSOR failed"); \
71#define NEW_NORM_TENSOR_FROM_HANDLE(_id, _attr, _dtype) do { \
73 _attr.is_const = FALSE; \
74 _attr.dtype.vx_type = _dtype; \
75 _id = vsi_nn_AddTensorFromHandle(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
76 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_NORM_TENSOR_FROM_HANDLE failed"); \
102 if (library::get().empty() ==
false)
104 if (
ready() ==
false)
LFATAL(
"Network is not ready");
105 std::vector<vsi_nn_tensor_attr_t> ret;
106 for (uint32_t i = 0; i < itsGraph->input.num; ++i)
108 ret.emplace_back(vsi_nn_GetTensor(itsGraph, itsGraph->input.tensors[i])->attr);
112 if (library::get().empty() ==
false && ret.back().dim_num >= 3)
114 vsi_nn_tensor_attr_t & attr = ret.back();
115 switch (attr.dtype.fmt)
117 case VSI_NN_DIM_FMT_NCHW:
118 if (attr.size[2] > attr.size[0] ) attr.dtype.fmt = VSI_NN_DIM_FMT_NHWC;
120 case VSI_NN_DIM_FMT_NHWC:
121 if (attr.size[0] > attr.size[1] ) attr.dtype.fmt = VSI_NN_DIM_FMT_NCHW;
138 if (library::get().empty() ==
false)
140 if (ready() ==
false)
LFATAL(
"Network is not ready");
141 std::vector<vsi_nn_tensor_attr_t> ret;
142 for (uint32_t i = 0; i < itsGraph->output.num; ++i)
143 ret.emplace_back(vsi_nn_GetTensor(itsGraph, itsGraph->output.tensors[i])->attr);
152void jevois::dnn::NetworkNPU::create_tensors(std::vector<vsi_nn_tensor_attr_t> & attrs, vsi_nn_node_t * node,
bool isin)
154 if (attrs.empty())
LFATAL(
"Invalid empty " << (isin ?
"in" :
"out") <<
"tensors specification");
156 for (
int tnum = 0; vsi_nn_tensor_attr_t & attr : attrs)
159 vsi_nn_tensor_id_t id;
165 node->input.tensors[tnum] = id;
166 itsGraph->input.tensors[tnum] = id;
171 node->output.tensors[tnum] = id;
172 itsGraph->output.tensors[tnum] = id;
183 if (itsGraph) vsi_nn_ReleaseGraph(&itsGraph);
184 if (itsCtx) vsi_nn_ReleaseContext(&itsCtx);
192 dataroot::freeze(doit);
194 library::freeze(doit);
195 intensors::freeze(doit);
196 outtensors::freeze(doit);
197 ovxver::freeze(doit);
205 if (itsGraph) { vsi_nn_ReleaseGraph(&itsGraph); itsGraph =
nullptr; }
208 if (itsCtx == 0) itsCtx = vsi_nn_CreateContext();
214 if (std::filesystem::exists(m) ==
false)
LFATAL(
"Missing network file " << m <<
" -- ABORT");
218 std::string libname = library::get();
224 size_t const numin = iattrs.size();
225 size_t const numout = oattrs.size();
228 itsGraph = vsi_nn_CreateGraph(itsCtx, numin + numout * 2, 1);
229 if (itsGraph == NULL)
LFATAL(
"Graph creation failed");
231 if (ovxver::get().empty() ==
false)
233 std::vector<std::string> tok =
jevois::split(ovxver::get(),
"\\.");
234 if (tok.size() != 3)
LFATAL(
"Malformed ovxver version [" << ovxver::get() <<
"] -- should be x.y.z");
235 vsi_nn_SetGraphVersion(itsGraph, std::stoi(tok[0]), std::stoi(tok[1]), std::stoi(tok[2]));
238 vsi_nn_SetGraphVersion(itsGraph, VSI_NN_VERSION_MAJOR, VSI_NN_VERSION_MINOR, VSI_NN_VERSION_PATCH);
240 vsi_nn_SetGraphInputs(itsGraph, NULL, numin);
241 vsi_nn_SetGraphOutputs(itsGraph, NULL, numout);
243 LINFO(
"Created graph with " << numin <<
" inputs and " << numout <<
" outputs");
246 vsi_nn_node_t * node[1];
247 NEW_VXNODE(node[0], VSI_NN_OP_NBG, numin, numout, 0);
248 node[0]->nn_param.nbg.type = VSI_NN_NBG_FILE;
249 node[0]->nn_param.nbg.url = m.c_str();
252 create_tensors(oattrs, node[0],
false);
253 create_tensors(iattrs, node[0],
true);
256 auto status = vsi_nn_SetupGraph(itsGraph, FALSE);
257 if (status != VSI_SUCCESS)
258 LFATAL(
"Failed to setup graph -- Possible causes:\n"
259 "- Incorrect intensors/outtensors in your YAML file?\n"
260 "- Wrong NPU model? Check --optimize VIPNANOQI_PID0X88\n"
261 "- Wrong NPU SDK version? Running ovxlib " <<
262 VSI_NN_VERSION_MAJOR <<
'.' << VSI_NN_VERSION_MINOR <<
'.' << VSI_NN_VERSION_PATCH);
269 if (std::filesystem::exists(libpath) ==
false)
LFATAL(
"Missing library file " << libpath <<
" -- ABORT");
272 typedef vsi_nn_graph_t*(signature)(
char const * ,
279 auto createmodel = itsLibLoader->load<signature>(
"vnn_CreateModel");
281 itsGraph = createmodel(m.c_str(), itsCtx,
nullptr, 0,
nullptr, 0);
285 if (itsGraph == NULL)
286 LFATAL(
"Graph creation using library failed:\n"
287 "- Wrong NPU model? Use kboard=VIM3 in convert\n"
288 "- Wrong NPU SDK version? Running ovxlib " <<
289 VSI_NN_VERSION_MAJOR <<
'.' << VSI_NN_VERSION_MINOR <<
'.' << VSI_NN_VERSION_PATCH);
290 LINFO(
"Graph successfully created using library.");
293 for (uint32_t i = 0; i < itsGraph->input.num; ++i)
294 LINFO(
"Input tensor " << i <<
": " <<
297 for (uint32_t i = 0; i < itsGraph->output.num; ++i)
298 LINFO(
"Output tensor " << i <<
": " <<
302 LINFO(
"Graph ready.");
304 if (verifygraph::get())
306 auto status = vsi_nn_VerifyGraph(itsGraph);
307 if (status != VSI_SUCCESS)
LFATAL(
"Graph verification failed -- \n"
308 "check that intensors/outtensors specs exactly match\n"
309 "those provided during model conversion.");
310 else LINFO(
"Graph verification ok");
319 static std::function<std::string(vsi_nn_graph_t *,
size_t, cv::Mat &)>
320 dequantize_one = [](vsi_nn_graph_t * graph,
size_t i, cv::Mat &
o) -> std::string
322 vsi_nn_tensor_t * ot = vsi_nn_GetTensor(graph, graph->output.tensors[i]);
323 vsi_nn_tensor_attr_t
const & oattr = ot->attr;
324 uint8_t * tensor_data = (uint8_t *)vsi_nn_ConvertTensorToData(graph, ot);
330 vsi_nn_Free(tensor_data);
339 std::vector<std::string> & info)
341 if (blobs.size() != itsGraph->input.num)
342 LFATAL(
"Received " << blobs.size() <<
" blobs, but network has " << itsGraph->input.num <<
" inputs");
349 for (
size_t b = 0; b < blobs.size(); ++b)
351 cv::Mat
const & blob = blobs[b];
354 vsi_nn_tensor_t * tensor = vsi_nn_GetTensor(itsGraph, itsGraph->input.tensors[b]);
355 if (tensor ==
nullptr)
LFATAL(
"Network does not have input tensor " << b);
356 auto const & iattr = tensor->attr;
364 auto status = vsi_nn_CopyDataToTensor(itsGraph, tensor, (uint8_t *)blob.data);
365 if (status != VSI_SUCCESS)
LFATAL(
"Error setting input tensor: " << status);
369 info.emplace_back(intimer.
stop());
373 auto status = vsi_nn_RunGraph(itsGraph);
374 if (status != VSI_SUCCESS)
LFATAL(
"Error running graph: " << status);
375 info.emplace_back(infertimer.
stop());
378 size_t const numouts = itsGraph->output.num;
379 if (numouts == 0)
return std::vector<cv::Mat>();
381 std::vector<cv::Mat> outs(numouts);
389 info.emplace_back(dequantize_one(itsGraph, 0, std::ref(outs[0])));
393 std::vector<std::future<std::string>> fvec;
395 for (uint32_t i = 0; i < numouts; ++i)
396 fvec.emplace_back(
jevois::async(dequantize_one, itsGraph, i, std::ref(outs[i])));
402 info.insert(info.end(), std::make_move_iterator(retvec.begin()), std::make_move_iterator(retvec.end()));
404 info.emplace_back(dqtimer.
stop());
409 for (uint32_t i = 0; i < numouts; ++i)
411 vsi_nn_tensor_t * ot = vsi_nn_GetTensor(itsGraph, itsGraph->output.tensors[i]);
412 vsi_nn_tensor_attr_t
const & oattr = ot->attr;
413 uint8_t * tensor_data = (uint8_t *)vsi_nn_ConvertTensorToData(itsGraph, ot);
418 outs[i] = rawout.clone();
423 vsi_nn_Free(tensor_data);
#define NEW_NORM_TENSOR(_id, _attr, _dtype)
#define NEW_VXNODE(_node, _type, _in, _out, _uid)
Class to open shared object (.so) files and load functions contained in them.
Simple one-shot timer class.
std::string stop(double *seconds)
End a time measurement period, report time spent as: 'prefix: ms (fps)' where % is replaced by values...
void start()
Start a time measurement period.
virtual std::vector< vsi_nn_tensor_attr_t > inputShapes() override
Get shapes of all input tensors.
virtual std::vector< vsi_nn_tensor_attr_t > outputShapes() override
Get shapes of all output tensors.
virtual ~NetworkNPU()
Destructor.
std::vector< cv::Mat > doprocess(std::vector< cv::Mat > const &blobs, std::vector< std::string > &info) override
Process input blobs and obtain output blobs.
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
void load() override
Load from disk.
bool ready()
Returns true when network is ready to run (loaded and initialized)
virtual void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
std::vector< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
std::string attrstr(vsi_nn_tensor_attr_t const &attr)
Get a string describing the specs of a tensor, including quantification specs (not provided by shapes...
cv::Mat attrmat(vsi_nn_tensor_attr_t const &attr, void *dataptr=nullptr)
Construct a cv::Mat from attr and possibly data pointer.
cv::Mat dequantize(cv::Mat const &m, vsi_nn_tensor_attr_t const &attr)
Dequantize an output to float32 according to the quantization spec in attr.
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.
bool attrmatch(vsi_nn_tensor_attr_t const &attr, cv::Mat const &blob)
Check that a cv::Mat blob matches exactly the spec of an attr.
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::filesystem::path absolutePath(std::filesystem::path const &root, std::filesystem::path const &path)
Compute an absolute path from two paths.
std::vector< std::string > split(std::string const &input, std::string const ®ex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
uint32_t postprocess_count
uint32_t graph_output_idx
vsi_nn_postprocess_base_t * postprocesses
vsi_nn_preprocess_base_t * preprocesses
uint32_t preprocess_count