JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Network.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2021 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 #include <jevois/DNN/Network.H>
19 #include <jevois/DNN/Utils.H>
20 #include <jevois/Util/Async.H>
21 
22 // ####################################################################################################
24 { }
25 
26 // ####################################################################################################
28 {
29  comment::freeze(doit);
30  url::freeze(doit);
31  extraintensors::freeze();
32 }
33 
34 // ####################################################################################################
35 void jevois::dnn::Network::onParamChange(network::outreshape const & JEVOIS_UNUSED_PARAM(param),
36  std::string const & val)
37 {
38  itsReshape.clear();
39  if (val.empty()) return;
40 
41  itsReshape = jevois::dnn::parseTensorSpecs(val);
42 }
43 
44 // ####################################################################################################
46 {
47  // Do not destroy a network that is loading, and do not throw...
48  size_t count = 0;
49  while (itsLoading.load())
50  {
51  std::this_thread::sleep_for(std::chrono::milliseconds(5));
52  try { if (ready()) break; } catch (...) { }
53  if (count++ == 200) { LINFO("Waiting for network load to complete..."); count = 0; }
54  }
55 }
56 
57 // ####################################################################################################
59 {
60  // If we are loaded, we are ready to process:
61  if (itsLoaded.load()) return true;
62 
63  // If we are loading, check whether loading is complete or threw, otherwise return false as we keep loading:
64  if (itsLoading.load())
65  {
66  if (itsLoadFut.valid() && itsLoadFut.wait_for(std::chrono::milliseconds(2)) == std::future_status::ready)
67  {
68  try { itsLoadFut.get(); itsLoaded.store(true); itsLoading.store(false); LINFO("Network loaded."); return true; }
69  catch (...) { itsLoading.store(false); jevois::warnAndRethrowException(); }
70  }
71  return false;
72  }
73 
74  // Otherwise, trigger an async load:
75  itsLoading.store(true);
76  itsLoadFut = jevois::async(std::bind(&jevois::dnn::Network::load, this));
77  LINFO("Loading network...");
78 
79  return false;
80 }
81 
82 // ####################################################################################################
83 std::vector<cv::Mat> jevois::dnn::Network::process(std::vector<cv::Mat> const & blobs,
84  std::vector<std::string> & info)
85 {
86  if (ready() == false) LFATAL("Network is not ready");
87 
88  std::vector<cv::Mat> outs;
89  std::string const c = comment::get();
90 
91  // Add any extra input tensors?
92  std::string const extra = extraintensors::get();
93  if (extra.empty() == false)
94  {
95  std::vector<cv::Mat> newblobs = blobs;
96 
97  std::vector<std::string> ins = jevois::split(extra, ",\\s*");
98  for (std::string const & in : ins)
99  {
100  vsi_nn_tensor_attr_t attr; memset(&attr, 0, sizeof(attr));
101 
102  std::vector<std::string> tok = jevois::split(in, ":");
103  if (tok.size() != 3)
104  LFATAL("Malformed extra tensor, need <type>:<shape>:val1 val2 ... valN (separate multiple tensors by comma)");
105 
106  // Decode type and convert to vsi, only those types that OpenCV can support:
107  if (tok[0] == "8U") attr.dtype.vx_type = VSI_NN_TYPE_UINT8;
108  else if (tok[0] == "8S") attr.dtype.vx_type = VSI_NN_TYPE_INT8;
109  else if (tok[0] == "16U") attr.dtype.vx_type = VSI_NN_TYPE_UINT16;
110  else if (tok[0] == "16S") attr.dtype.vx_type = VSI_NN_TYPE_INT16;
111  else if (tok[0] == "16F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT16;
112  else if (tok[0] == "32S") attr.dtype.vx_type = VSI_NN_TYPE_INT32;
113  else if (tok[0] == "32F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT32;
114  else if (tok[0] == "64F") attr.dtype.vx_type = VSI_NN_TYPE_FLOAT64;
115  else throw std::range_error("Unsupported extra input tensor type [" + tok[0] + "] in " + extra);
116 
117  // Decode the dims:
118  std::vector<size_t> dims = jevois::dnn::strshape(tok[1]);
119  attr.dim_num = dims.size();
120  for (size_t i = 0; i < attr.dim_num; ++i) attr.size[attr.dim_num - 1 - i] = dims[i];
121 
122  // Allocate the tensor:
123  attr.dtype.qnt_type = VSI_NN_QNT_TYPE_NONE;
124  attr.dtype.fmt = VSI_NN_DIM_FMT_AUTO;
125  cv::Mat b = jevois::dnn::attrmat(attr);
126 
127  // Populate the values:
128  std::vector<std::string> vals = jevois::split(tok[2], "\\s+");
129  size_t const nvals = vals.size();
130  if (nvals != b.total())
131  LFATAL("Extra in tensor needs " << b.total() << " values, but " << nvals << " given in [" << in << ']');
132  switch (attr.dtype.vx_type)
133  {
134  case VSI_NN_TYPE_UINT8:
135  {
136  uint8_t * ptr = reinterpret_cast<uint8_t *>(b.data);
137  for (std::string const & v : vals) *ptr++ = std::stoi(v);
138  }
139  break;
140 
141  case VSI_NN_TYPE_INT8:
142  {
143  int8_t * ptr = reinterpret_cast<int8_t *>(b.data);
144  for (std::string const & v : vals) *ptr++ = std::stoi(v);
145  }
146  break;
147 
148  case VSI_NN_TYPE_UINT16:
149  {
150  uint16_t * ptr = reinterpret_cast<uint16_t *>(b.data);
151  for (std::string const & v : vals) *ptr++ = std::stoi(v);
152  }
153  break;
154 
155  case VSI_NN_TYPE_INT16:
156  {
157  int16_t * ptr = reinterpret_cast<int16_t *>(b.data);
158  for (std::string const & v : vals) *ptr++ = std::stoi(v);
159  }
160  break;
161 
162  case VSI_NN_TYPE_FLOAT16:
163  {
164  cv::float16_t * ptr = reinterpret_cast<cv::float16_t *>(b.data);
165  for (std::string const & v : vals) *ptr++ = cv::float16_t(std::stof(v));
166  }
167  break;
168 
169  case VSI_NN_TYPE_INT32:
170  {
171  int32_t * ptr = reinterpret_cast<int32_t *>(b.data);
172  for (std::string const & v : vals) *ptr++ = std::stoi(v);
173  }
174  break;
175 
176  case VSI_NN_TYPE_FLOAT32:
177  {
178  float * ptr = reinterpret_cast<float *>(b.data);
179  for (std::string const & v : vals) *ptr++ = std::stof(v);
180  }
181  break;
182 
183  case VSI_NN_TYPE_FLOAT64:
184  {
185  double * ptr = reinterpret_cast<double *>(b.data);
186  for (std::string const & v : vals) *ptr++ = std::stod(v);
187  }
188  break;
189 
190  default: LFATAL("internal inconsistency");
191  }
192 
193  newblobs.emplace_back(std::move(b));
194  }
195 
196  // NOTE: Keep the code below in sync with the default case (no extra inputs). Both branches are duplicated to avoid
197  // having to make a copy of blobs into newblobs in the standard case when we do not have any extra inputs:
198 
199  // Show info about input tensors:
200  info.emplace_back("* Input Tensors");
201  for (cv::Mat const & b : newblobs) info.emplace_back("- " + jevois::dnn::shapestr(b));
202 
203  // Run processing on the derived class:
204  info.emplace_back("* Network");
205  if (c.empty() == false) info.emplace_back(c);
206 
207  outs = std::move(doprocess(newblobs, info));
208  }
209  else
210  {
211  // Show info about input tensors:
212  info.emplace_back("* Input Tensors");
213  for (cv::Mat const & b : blobs) info.emplace_back("- " + jevois::dnn::shapestr(b));
214 
215  // Run processing on the derived class:
216  info.emplace_back("* Network");
217  if (c.empty() == false) info.emplace_back(c);
218 
219  outs = std::move(doprocess(blobs, info));
220  }
221 
222  // Show info about output tensors:
223  info.emplace_back("* Output Tensors");
224  for (size_t i = 0; i < outs.size(); ++i) info.emplace_back("- " + jevois::dnn::shapestr(outs[i]));
225 
226  // Possibly reshape the tensors:
227  if (itsReshape.empty() == false)
228  {
229  if (itsReshape.size() != outs.size())
230  LFATAL("Received " << outs.size() << " but outreshape has " << itsReshape.size() << " entries");
231 
232  info.emplace_back("* Reshaped Output Tensors");
233  for (size_t i = 0; i < outs.size(); ++i)
234  {
235  outs[i] = outs[i].reshape(1, jevois::dnn::attrdims(itsReshape[i]));
236  info.emplace_back("- " + jevois::dnn::shapestr(outs[i]));
237  }
238  }
239 
240  return outs;
241 }
jevois::imu::get
Data collection mode RAW means that the latest available raw data is returned each time get() is called
jevois::async
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.
Async.H
jevois::dnn::Network::ready
bool ready()
Returns true when network is ready to run (loaded and initialized)
Definition: Network.C:58
jevois::dnn::Network::~Network
virtual ~Network()
Destructor.
Definition: Network.C:23
jevois::dnn::Network::onParamChange
void onParamChange(network::outreshape const &param, std::string const &val) override
Definition: Network.C:35
jevois::split
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
Network.H
jevois::warnAndRethrowException
void warnAndRethrowException(std::string const &prefix="")
Convenience function to catch an exception, issue some LERROR (depending on type),...
Definition: Log.C:203
jevois::dnn::Network::process
std::vector< cv::Mat > process(std::vector< cv::Mat > const &blobs, std::vector< std::string > &info)
Process input blobs and obtain output blobs.
Definition: Network.C:83
Utils.H
jevois::dnn::Network::waitBeforeDestroy
void waitBeforeDestroy()
If network is currently loading, wait until that is done before destroying.
Definition: Network.C:45
jevois::dnn::Network::load
virtual void load()=0
Load from disk.
jevois::dnn::Network::freeze
virtual void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
Definition: Network.C:27
jevois::dnn::parseTensorSpecs
std::vector< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
Definition: Utils.C:386
jevois::dnn::shapestr
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:104
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
jevois::dnn::attrdims
std::vector< int > attrdims(vsi_nn_tensor_attr_t const &attr)
Get a tensor dims as a vector of int, useful to construct a matching cv::Mat.
Definition: Utils.C:477
jevois::dnn::strshape
std::vector< size_t > strshape(std::string const &str)
Get a vector of size_t from a string containing AxBxC...
Definition: Utils.C:276
jevois::dnn::attrmat
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:470
LINFO
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
Definition: Log.H:194