JeVois  1.21
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
NetworkNPU.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#ifdef JEVOIS_PLATFORM_PRO
19
21#include <jevois/Util/Utils.H>
22#include <jevois/DNN/Utils.H>
23#include <vsi_nn_version.h>
24#include <filesystem>
25
26#define VNN_APP_DEBUG (FALSE)
27
28/*-------------------------------------------
29 Macros
30 -------------------------------------------*/
31
32#define NEW_VXNODE(_node, _type, _in, _out, _uid) do { \
33 _node = vsi_nn_AddNode(itsGraph, _type, _in, _out, NULL); \
34 _node->uid = (uint32_t)_uid; \
35 if (NULL == _node) LFATAL("NEW_VXNODE failed"); \
36 } while(0)
37
38#define NEW_VIRTUAL_TENSOR(_id, _attr, _dtype) do { \
39 memset(_attr.size, 0, VSI_NN_MAX_DIM_NUM * sizeof(vsi_size_t)); \
40 _attr.dim_num = VSI_NN_DIM_AUTO; \
41 _attr.vtl = !VNN_APP_DEBUG; \
42 _attr.is_const = FALSE; \
43 _attr.dtype.vx_type = _dtype; \
44 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
45 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_VIRTUAL_TENSOR failed"); \
46 } while(0)
47
48// Set const tensor dims out of this macro.
49#define NEW_CONST_TENSOR(_id, _attr, _dtype, _ofst, _size) do { \
50 data = load_data(fp, _ofst, _size); \
51 _attr.vtl = FALSE; \
52 _attr.is_const = TRUE; \
53 _attr.dtype.vx_type = _dtype; \
54 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, data); \
55 free(data); \
56 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_CONST_TENSOR failed"); \
57 } while(0)
58
59// Set generic tensor dims out of this macro.
60#define NEW_NORM_TENSOR(_id, _attr, _dtype) do { \
61 _attr.vtl = FALSE; \
62 _attr.is_const = FALSE; \
63 _attr.dtype.vx_type = _dtype; \
64 _id = vsi_nn_AddTensor(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
65 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_NORM_TENSOR failed"); \
66 } while(0)
67
68// Set generic tensor dims out of this macro.
69#define NEW_NORM_TENSOR_FROM_HANDLE(_id, _attr, _dtype) do { \
70 _attr.vtl = FALSE; \
71 _attr.is_const = FALSE; \
72 _attr.dtype.vx_type = _dtype; \
73 _id = vsi_nn_AddTensorFromHandle(itsGraph, VSI_NN_TENSOR_ID_AUTO, & _attr, NULL); \
74 if (VSI_NN_TENSOR_ID_NA == _id) LFATAL("NEW_NORM_TENSOR_FROM_HANDLE failed"); \
75 } while(0)
76
77// ####################################################################################################
78std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkNPU::inputShapes()
79{
80 return jevois::dnn::parseTensorSpecs(intensors::get());
81}
82
83// ####################################################################################################
84std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkNPU::outputShapes()
85{
86 return jevois::dnn::parseTensorSpecs(outtensors::get());
87}
88
89// ####################################################################################################
90void jevois::dnn::NetworkNPU::create_tensors(std::vector<vsi_nn_tensor_attr_t> & attrs, vsi_nn_node_t * node, bool isin)
91{
92 if (attrs.empty()) LFATAL("Invalid empty " << (isin ? "in" : "out") << "tensors specification");
93
94 for (int tnum = 0; vsi_nn_tensor_attr_t & attr : attrs)
95 {
96 // Allocate the tensor:
97 vsi_nn_tensor_id_t id;
98 NEW_NORM_TENSOR(id, attr, attr.dtype.vx_type);
99
100 // Connect the tensor:
101 if (isin)
102 {
103 node->input.tensors[tnum] = id;
104 itsGraph->input.tensors[tnum] = id;
105 LINFO("Input tensor " << tnum << ": " << jevois::dnn::attrstr(attr));
106 }
107 else
108 {
109 node->output.tensors[tnum] = id;
110 itsGraph->output.tensors[tnum] = id;
111 LINFO("Output tensor " << tnum << ": " << jevois::dnn::attrstr(attr));
112 }
113 ++tnum;
114 }
115}
116
117// ####################################################################################################
119{
120 waitBeforeDestroy();
121 if (itsGraph) vsi_nn_ReleaseGraph(&itsGraph);
122 if (itsCtx) vsi_nn_ReleaseContext(&itsCtx);
123}
124
125// ####################################################################################################
127{
128 dataroot::freeze(doit);
129 model::freeze(doit);
130 intensors::freeze(doit);
131 outtensors::freeze(doit);
132 ovxver::freeze(doit);
133 jevois::dnn::Network::freeze(doit); // base class parameters
134}
135
136// ####################################################################################################
138{
139 // Need to nuke the network first if it exists or we could run out of RAM:
140 if (itsGraph) { vsi_nn_ReleaseGraph(&itsGraph); itsGraph = nullptr; }
141
142 // Create context if needed:
143 if (itsCtx == 0) itsCtx = vsi_nn_CreateContext();
144
145 // Parse input and output tensor specs:
146 std::vector<vsi_nn_tensor_attr_t> iattrs = jevois::dnn::parseTensorSpecs(intensors::get());
147 std::vector<vsi_nn_tensor_attr_t> oattrs = jevois::dnn::parseTensorSpecs(outtensors::get());
148 size_t const numin = iattrs.size();
149 size_t const numout = oattrs.size();
150
151 // Create graph:
152 itsGraph = vsi_nn_CreateGraph(itsCtx, numin + numout * 2, 1);
153 if (itsGraph == NULL) LFATAL("Graph creation failed");
154
155 if (ovxver::get().empty() == false)
156 {
157 std::vector<std::string> tok = jevois::split(ovxver::get(), "\\.");
158 if (tok.size() != 3) LFATAL("Malformed ovxver version [" << ovxver::get() <<"] -- should be x.y.z");
159 vsi_nn_SetGraphVersion(itsGraph, std::stoi(tok[0]), std::stoi(tok[1]), std::stoi(tok[2]));
160 }
161 else
162 vsi_nn_SetGraphVersion(itsGraph, VSI_NN_VERSION_MAJOR, VSI_NN_VERSION_MINOR, VSI_NN_VERSION_PATCH);
163
164 vsi_nn_SetGraphInputs(itsGraph, NULL, numin);
165 vsi_nn_SetGraphOutputs(itsGraph, NULL, numout);
166
167 LINFO("Created graph with " << numin << " inputs and " << numout << " outputs");
168
169 // Get NBG file name:
170 std::string const m = jevois::absolutePath(dataroot::get(), model::get());
171
172 // Check that the file does exist so we avoid cryptic errors if not:
173 if (std::filesystem::exists(m) == false) LFATAL("Missing network file " << m << " -- ABORT");
174
175 // Initialize node:
176 vsi_nn_node_t * node[1];
177 NEW_VXNODE(node[0], VSI_NN_OP_NBG, numin, numout, 0);
178 node[0]->nn_param.nbg.type = VSI_NN_NBG_FILE;
179 node[0]->nn_param.nbg.url = m.c_str();
180
181 // Create input and output tensors and attach them to the node and graph:
182 create_tensors(oattrs, node[0], false);
183 create_tensors(iattrs, node[0], true);
184
185 // Setup the graph:
186 auto status = vsi_nn_SetupGraph(itsGraph, FALSE);
187 if (status != VSI_SUCCESS)
188 LFATAL("Failed to setup graph -- Possible causes:\n"
189 "- Incorrect intensors/outtensors in your YAML file?\n"
190 "- Wrong NPU model? Check --optimize VIPNANOQI_PID0X88\n"
191 "- Wrong NPU SDK version? Running ovxlib " <<
192 VSI_NN_VERSION_MAJOR << '.' << VSI_NN_VERSION_MINOR << '.' << VSI_NN_VERSION_PATCH);
193 LINFO("Graph ready.");
194
195 if (verifygraph::get())
196 {
197 status = vsi_nn_VerifyGraph(itsGraph);
198 if (status != VSI_SUCCESS) LFATAL("Graph verification failed -- \n"
199 "check that intensors/outtensors specs exactly match\n"
200 "those provided during model conversion.");
201 else LINFO("Graph verification ok");
202 }
203}
204
205// ####################################################################################################
206std::vector<cv::Mat> jevois::dnn::NetworkNPU::doprocess(std::vector<cv::Mat> const & blobs,
207 std::vector<std::string> & info)
208{
209 if (blobs.size() != itsGraph->input.num)
210 LFATAL("Received " << blobs.size() << " blobs, but network has " << itsGraph->input.num << " inputs");
211
212 for (size_t b = 0; b < blobs.size(); ++b)
213 {
214 cv::Mat const & blob = blobs[b];
215
216 // Get the input tensor:
217 vsi_nn_tensor_t * tensor = vsi_nn_GetTensor(itsGraph, itsGraph->input.tensors[b]);
218 if (tensor == nullptr) LFATAL("Network does not have input tensor " << b);
219 auto const & iattr = tensor->attr;
220
221 // Check that blob and tensor are a complete match:
222 if (jevois::dnn::attrmatch(iattr, blob) == false)
223 LFATAL("Input " << b << ": received " << jevois::dnn::shapestr(blob) <<
224 " but want: " << jevois::dnn::shapestr(iattr));
225
226 // Copy blob data to tensor:
227 auto status = vsi_nn_CopyDataToTensor(itsGraph, tensor, (uint8_t *)blob.data);
228 if (status != VSI_SUCCESS) LFATAL("Error setting input tensor: " << status);
229
230 info.emplace_back("- In " + std::to_string(b) + ": " + jevois::dnn::attrstr(iattr));
231 }
232
233 // Ok, let's run the network:
234 auto status = vsi_nn_RunGraph(itsGraph);
235 if (status != VSI_SUCCESS) LFATAL("Error running graph: " << status);
236 info.emplace_back("- Network forward pass ok");
237
238 // Collect the outputs:
239 std::vector<cv::Mat> outs;
240 for (uint32_t i = 0; i < itsGraph->output.num; ++i)
241 {
242 vsi_nn_tensor_t * ot = vsi_nn_GetTensor(itsGraph, itsGraph->output.tensors[i]);
243 vsi_nn_tensor_attr_t const & oattr = ot->attr;
244 uint8_t * tensor_data = (uint8_t *)vsi_nn_ConvertTensorToData(itsGraph, ot);
245
246 try
247 {
248 cv::Mat rawout = jevois::dnn::attrmat(oattr, tensor_data);
249
250 if (dequant::get())
251 {
252 outs.emplace_back(jevois::dnn::dequantize(rawout, oattr));
253 info.emplace_back("- Out " + std::to_string(i) + ": " + jevois::dnn::attrstr(oattr) + " -> 32F");
254 }
255 else
256 {
257 outs.emplace_back(rawout.clone());
258 info.emplace_back("- Out " + std::to_string(i) + ": " + jevois::dnn::attrstr(oattr));
259 }
260 } catch (...) { vsi_nn_Free(tensor_data); jevois::warnAndRethrowException(); }
261
262 vsi_nn_Free(tensor_data);
263 }
264
265 return outs;
266}
267
268#endif // JEVOIS_PLATFORM_PRO
#define NEW_NORM_TENSOR(_id, _attr, _dtype)
Definition NetworkNPU.C:60
#define NEW_VXNODE(_node, _type, _in, _out, _uid)
Definition NetworkNPU.C:32
virtual std::vector< vsi_nn_tensor_attr_t > inputShapes() override
Get shapes of all input tensors.
Definition NetworkNPU.C:78
virtual std::vector< vsi_nn_tensor_attr_t > outputShapes() override
Get shapes of all output tensors.
Definition NetworkNPU.C:84
virtual ~NetworkNPU()
Destructor.
Definition NetworkNPU.C:118
std::vector< cv::Mat > doprocess(std::vector< cv::Mat > const &blobs, std::vector< std::string > &info) override
Process input blobs and obtain output blobs.
Definition NetworkNPU.C:206
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
Definition NetworkNPU.C:126
void load() override
Load from disk.
Definition NetworkNPU.C:137
virtual void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
Definition Network.C:27
#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< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
Definition Utils.C:387
std::string attrstr(vsi_nn_tensor_attr_t const &attr)
Get a string describing the specs of a tensor, including quantification specs (not provided by shapes...
Definition Utils.C:512
cv::Mat attrmat(vsi_nn_tensor_attr_t const &attr, void *dataptr=nullptr)
Construct a cv::Mat from attr and possibly data pointer.
Definition Utils.C:471
cv::Mat dequantize(cv::Mat const &m, vsi_nn_tensor_attr_t const &attr)
Dequantize an output to float32 according to the quantization spec in attr.
Definition Utils.C:793
std::string shapestr(cv::Mat const &m)
Get a string of the form: "nD AxBxC... TYPE" from an n-dimensional cv::Mat with data type TYPE.
Definition Utils.C:105
bool attrmatch(vsi_nn_tensor_attr_t const &attr, cv::Mat const &blob)
Check that a cv::Mat blob matches exactly the spec of an attr.
Definition Utils.C:704
std::filesystem::path absolutePath(std::filesystem::path const &root, std::filesystem::path const &path)
Compute an absolute path from two paths.
Definition Utils.C:385
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