JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
NetworkTPU.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_PRO
19
21#include <jevois/DNN/Utils.H>
22#include <jevois/Util/Utils.H>
23
24#include <edgetpu_c.h>
25
26#include <tensorflow/lite/builtin_op_data.h>
27#include <tensorflow/lite/kernels/register.h>
28#include <tensorflow/lite/kernels/internal/tensor_ctypes.h> // for GetTensorData()
29
30// ####################################################################################################
31int jevois::dnn::NetworkTPU::ErrorReporter::Report(char const * format, va_list args)
32{
33 static char buf[2048];
34 int ret = vsnprintf(buf, 2048, format, args);
35 itsErrors.push_back(buf);
36 LERROR(buf);
37 return ret;
38}
39
40// ####################################################################################################
43
44// ####################################################################################################
46{
47 dataroot::freeze(doit);
48 model::freeze(doit);
49 tpunum::freeze(doit);
50 dequant::freeze(doit);
51 intensors::freeze(doit);
52 outtensors::freeze(doit);
53 jevois::dnn::Network::freeze(doit); // base class parameters
54}
55
56// ####################################################################################################
57std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkTPU::inputShapes()
58{
59 if (ready() == false) LFATAL("Network is not ready");
60
61 // Shapes are embedded in the network file, but can be overridden:
62 std::string const inshapes = intensors::get();
63 if (inshapes.empty() == false) return jevois::dnn::parseTensorSpecs(inshapes);
64
65 // Get the shapes from the network:
66 std::vector<vsi_nn_tensor_attr_t> ret;
67 auto const & input_indices = itsInterpreter->inputs();
68
69 for (size_t i = 0; i < input_indices.size(); ++i)
70 {
71 TfLiteTensor const * itensor = itsInterpreter->tensor(input_indices[i]);
72 if (itensor == nullptr) LFATAL("Network has Null input tensor " << i);
73 ret.emplace_back(jevois::dnn::tensorattr(itensor));
74 LINFO("Input " << i << ": " << jevois::dnn::attrstr(ret.back()));
75 }
76 return ret;
77}
78
79// ####################################################################################################
80std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkTPU::outputShapes()
81{
82 if (ready() == false) LFATAL("Network is not ready");
83
84 // Shapes are embedded in the network file, but can be overridden:
85 std::string const outshapes = outtensors::get();
86 if (outshapes.empty() == false) return jevois::dnn::parseTensorSpecs(outshapes);
87
88 // Get the shapes from the network:
89 std::vector<vsi_nn_tensor_attr_t> ret;
90 auto const & output_indices = itsInterpreter->outputs();
91
92 for (size_t i = 0; i < output_indices.size(); ++i)
93 {
94 TfLiteTensor const * otensor = itsInterpreter->tensor(output_indices[i]);
95 if (otensor == nullptr) LFATAL("Network has Null output tensor " << i);
96 ret.emplace_back(jevois::dnn::tensorattr(otensor));
97 LINFO("Output " << i << ": " << jevois::dnn::attrstr(ret.back()));
98 }
99 return ret;
100}
101
102// ####################################################################################################
104{
105 // Need to nuke the network first if it exists or we could run out of RAM:
106 itsInterpreter.reset();
107 itsModel.reset();
108 itsErrorReporter.itsErrors.clear();
109
110 std::string const m = jevois::absolutePath(dataroot::get(), model::get());
111
112 try
113 {
114 // Create and load the network:
115 itsModel = tflite::FlatBufferModel::BuildFromFile(m.c_str(), &itsErrorReporter);
116 if (!itsModel) LFATAL("Failed to load model from file " << m);
117
118 tflite::ops::builtin::BuiltinOpResolver resolver;
119 tflite::InterpreterBuilder(*itsModel, resolver)(&itsInterpreter);
120
121 size_t num_devices;
122 std::unique_ptr<edgetpu_device, decltype(&edgetpu_free_devices)>
123 devices(edgetpu_list_devices(&num_devices), &edgetpu_free_devices);
124
125 if (num_devices == 0) LFATAL("No connected TPU found");
126 size_t const tn = tpunum::get();
127 if (tn >= num_devices) LFATAL("Cannot use TPU " << tn << " because only " << num_devices << " TPUs detected.");
128
129 auto const & device = devices.get()[tn];
130 itsInterpreter->
131 ModifyGraphWithDelegate(std::unique_ptr<TfLiteDelegate, decltype(&edgetpu_free_delegate)>
132 (edgetpu_create_delegate(device.type, device.path, nullptr, 0), &edgetpu_free_delegate));
133
134 itsInterpreter->SetNumThreads(1);
135
136 if (itsInterpreter->AllocateTensors() != kTfLiteOk) LFATAL("Failed to allocate tensors");
137
138 for (size_t i = 0; i < itsInterpreter->inputs().size(); ++i)
139 LINFO("Input tensor " << i << ": " << itsInterpreter->GetInputName(i));
140 for (size_t i = 0; i < itsInterpreter->outputs().size(); ++i)
141 LINFO("Output tensor " << i << ": " << itsInterpreter->GetOutputName(i));
142
143 int t_size = itsInterpreter->tensors_size();
144 for (int i = 0; i < t_size; ++i)
145 if (itsInterpreter->tensor(i)->name)
146 LINFO("Layer " << i << ": " << itsInterpreter->tensor(i)->name << ", "
147 << jevois::dnn::shapestr(itsInterpreter->tensor(i)) << ", "
148 << itsInterpreter->tensor(i)->bytes << " bytes, scale: "
149 << itsInterpreter->tensor(i)->params.scale << ", zero: "
150 << itsInterpreter->tensor(i)->params.zero_point);
151
152 //if (threads::get()) itsInterpreter->SetNumThreads(threads::get());
153 for (size_t i = 0; i < itsInterpreter->inputs().size(); ++i)
154 LINFO("input " << i << " is layer " << itsInterpreter->inputs()[i]);
155 for (size_t i = 0; i < itsInterpreter->outputs().size(); ++i)
156 LINFO("output " << i << " is layer " << itsInterpreter->outputs()[i]);
157 }
158 catch (std::exception const & e)
159 {
160 std::string err = "\n";
161 for (std::string const & s : itsErrorReporter.itsErrors) err += "ERR " + s + "\n";
162 err += e.what();
163 throw std::runtime_error(err);
164 }
165}
166
167// ####################################################################################################
168std::vector<cv::Mat> jevois::dnn::NetworkTPU::doprocess(std::vector<cv::Mat> const & blobs,
169 std::vector<std::string> & info)
170{
171 if ( ! itsInterpreter) LFATAL("Internal inconsistency");
172
173 if (blobs.size() != itsInterpreter->inputs().size())
174 LFATAL("Received " << blobs.size() << " input tensors, but network wants " << itsInterpreter->inputs().size());
175
176 auto const & input_indices = itsInterpreter->inputs();
177 for (size_t b = 0; b < blobs.size(); ++b)
178 {
179 cv::Mat const & cvin = blobs[b];
180 auto * itensor = itsInterpreter->tensor(input_indices[b]);
181 if (itensor == nullptr) LFATAL("Network has Null input tensor " << b);
182
183 // Make sure input dims are a match:
184 TfLiteIntArray const & tfindims = *itensor->dims;
185 cv::MatSize const & cvindims = cvin.size;
186 for (int i = 0; i < tfindims.size; ++i)
187 if (tfindims.data[i] != cvindims[i])
188 LFATAL("Input " << b << " mismatch: blob is " << jevois::dnn::shapestr(cvin) <<
189 " but network wants " << jevois::dnn::shapestr(itensor));
190
191 // Make sure total sizes in bytes are a match too:
192 size_t const cvsiz = cvin.total() * cvin.elemSize();
193 size_t const tfsiz = itensor->bytes;
194 if (cvsiz != tfsiz) LFATAL("Input " << b << " size mismatch: blob has " << cvsiz <<
195 " but network wants " << tfsiz << " bytes. Maybe type is wrong in intensors?");
196
197 // Copy input blob to input tensor:
198 uint8_t * input = tflite::GetTensorData<uint8_t>(itensor);
199 if (input == nullptr) LFATAL("Input tensor " << b << " is null in network");
200 std::memcpy(input, cvin.data, cvsiz);
201 info.emplace_back("- Input tensors ok");
202 }
203
204 // Run the network:
205 if (itsInterpreter->Invoke() != kTfLiteOk) LFATAL("Failed to invoke interpreter");
206 info.emplace_back("- Network forward pass ok");
207
208 // Collect/convert the outputs:
209 auto const & output_indices = itsInterpreter->outputs();
210 std::vector<cv::Mat> outs;
211
212 for (size_t o = 0; o < output_indices.size(); ++o)
213 {
214 auto const * otensor = itsInterpreter->tensor(output_indices[o]);
215 if (otensor == nullptr) LFATAL("Network produced Null output tensor " << o);
216
217 // Allocate an OpenCV output array of dims that match our output tensor:
218 TfLiteIntArray const & tfdims = *otensor->dims;
219 std::vector<int> cvdims; size_t sz = 1;
220 for (int i = 0; i < tfdims.size; ++i) { cvdims.emplace_back(tfdims.data[i]); sz *= tfdims.data[i]; }
221
222 // Convert/copy output tensor data to OpenCV arrays:
223 TfLiteType const ot = otensor->type;
224 std::string const otname = TfLiteTypeGetName(ot);
225 bool notdone = true;
226
227 if (dequant::get())
228 {
229 switch (ot)
230 {
231 case kTfLiteUInt8:
232 {
233 // Dequantize UINT8 to FLOAT32:
234 uint8_t const * output = tflite::GetTensorData<uint8_t>(otensor);
235 if (output == nullptr) LFATAL("Network produced Null output tensor data " << o);
236 cv::Mat const cvi(cvdims, CV_8U, (void *)output);
237 cv::Mat cvout; cvi.convertTo(cvout, CV_32F);
238 if (otensor->params.zero_point) cvout -= otensor->params.zero_point;
239 if (otensor->params.scale != 1.0F) cvout *= otensor->params.scale;
240 info.emplace_back("- Dequantized " + otname + " output tensor " + std::to_string(o) + " to FLOAT32");
241 outs.emplace_back(cvout);
242 notdone = false;
243 }
244 break;
245
246 default:
247 // For now, we only know how to dequantize uint8...
248 break;
249 }
250 }
251
252 if (notdone)
253 {
254 // We just want to copy the data untouched, except that OpenCV does not support as many pixel types as tensorflow:
255 switch (ot)
256 {
257 case kTfLiteInt64: // used by DeepLabV3. Just convert to int32:
258 {
259 cv::Mat cvout(cvdims, CV_32S);
260 int * cvoutdata = (int *)cvout.data;
261 int64_t const * output = tflite::GetTensorData<int64_t>(otensor);
262 if (output == nullptr) LFATAL("Network produced Null output tensor data " << o);
263 for (size_t i = 0; i < sz; ++i) *cvoutdata++ = int(*output++);
264 info.emplace_back("- Converted " + otname + " output tensor " + std::to_string(o) + " to INT32");
265 outs.emplace_back(cvout);
266 }
267 break;
268
269 case kTfLiteFloat32:
270 case kTfLiteInt32:
271 case kTfLiteUInt8:
272 case kTfLiteInt16:
273 case kTfLiteInt8:
274 case kTfLiteFloat16:
275 case kTfLiteFloat64:
276 {
277 // Simple copy with no conversion:
278 unsigned int cvtype = jevois::dnn::tf2cv(ot);
279 cv::Mat cvout(cvdims, cvtype);
280 uint8_t const * output = tflite::GetTensorData<uint8_t>(otensor);
281 if (output == nullptr) LFATAL("Network produced Null output tensor data " << o);
282 std::memcpy(cvout.data, output, sz * jevois::cvBytesPerPix(cvtype));
283 info.emplace_back("- Copied " + otname + " output tensor " + std::to_string(o));
284 outs.emplace_back(cvout);
285 }
286 break;
287
288 default:
289 LFATAL("Output tensor " << otensor->name << " has unsupported type: " << otname);
290 }
291 }
292 }
293
294 // Report the TPU temperature:
295 static int temp = 0;
296 size_t const tn = tpunum::get();
297 if ((jevois::frameNum() % 50) == 0)
298 try { temp = std::stoi(jevois::getFileString(jevois::sformat("/sys/class/apex/apex_%zu/temp", tn).c_str())); }
299 catch (...) { } // silently ignore any errors
300 info.emplace_back(jevois::sformat("- TPU%zu temp %dC", tn, temp / 1000));
301
302 return outs;
303}
304
305#endif // JEVOIS_PRO
#define o
Definition Font10x20.C:6
void load() override
Load from disk.
Definition NetworkTPU.C:103
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
Definition NetworkTPU.C:45
virtual ~NetworkTPU()
Destructor.
Definition NetworkTPU.C:41
virtual std::vector< vsi_nn_tensor_attr_t > outputShapes() override
Get shapes of all output tensors.
Definition NetworkTPU.C:80
virtual std::vector< vsi_nn_tensor_attr_t > inputShapes() override
Get shapes of all input tensors.
Definition NetworkTPU.C:57
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 NetworkTPU.C:168
void waitBeforeDestroy()
If network is currently loading, wait until that is done before destroying.
Definition Network.C:152
virtual void freeze(bool doit)
Freeze/unfreeze parameters that users should not change while running.
Definition Network.C:32
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition Log.H:230
#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
int tf2cv(TfLiteType t)
Convert from TensorFlow data type to OpenCV.
Definition Utils.C:310
vsi_nn_tensor_attr_t tensorattr(TfLiteTensor const *t)
Get tensor shape and type attributes for a TensorFlow Lite tensor.
Definition Utils.C:588
std::vector< vsi_nn_tensor_attr_t > parseTensorSpecs(std::string const &specs)
Parse tensor specification.
Definition Utils.C:411
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:536
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
unsigned int cvBytesPerPix(unsigned int cvtype)
Return the number of bytes per pixel for a given OpenCV pixel type.
Definition Utils.C:89
std::string getFileString(char const *fname, int skip=0)
Read one line from a file and return it as a string.
Definition Utils.C:542
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
Definition Utils.C:440
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