26#include <tensorflow/lite/builtin_op_data.h>
27#include <tensorflow/lite/kernels/register.h>
28#include <tensorflow/lite/kernels/internal/tensor_ctypes.h>
31int jevois::dnn::NetworkTPU::ErrorReporter::Report(
char const * format, va_list args)
33 static char buf[2048];
34 int ret = vsnprintf(buf, 2048, format, args);
35 itsErrors.push_back(buf);
47 dataroot::freeze(doit);
50 dequant::freeze(doit);
51 intensors::freeze(doit);
52 outtensors::freeze(doit);
59 if (ready() ==
false)
LFATAL(
"Network is not ready");
62 std::string
const inshapes = intensors::get();
66 std::vector<vsi_nn_tensor_attr_t> ret;
67 auto const & input_indices = itsInterpreter->inputs();
69 for (
size_t i = 0; i < input_indices.size(); ++i)
71 TfLiteTensor
const * itensor = itsInterpreter->tensor(input_indices[i]);
72 if (itensor ==
nullptr)
LFATAL(
"Network has Null input tensor " << i);
82 if (ready() ==
false)
LFATAL(
"Network is not ready");
85 std::string
const outshapes = outtensors::get();
89 std::vector<vsi_nn_tensor_attr_t> ret;
90 auto const & output_indices = itsInterpreter->outputs();
92 for (
size_t i = 0; i < output_indices.size(); ++i)
94 TfLiteTensor
const * otensor = itsInterpreter->tensor(output_indices[i]);
95 if (otensor ==
nullptr)
LFATAL(
"Network has Null output tensor " << i);
106 itsInterpreter.reset();
108 itsErrorReporter.itsErrors.clear();
115 itsModel = tflite::FlatBufferModel::BuildFromFile(m.c_str(), &itsErrorReporter);
116 if (!itsModel)
LFATAL(
"Failed to load model from file " << m);
118 tflite::ops::builtin::BuiltinOpResolver resolver;
119 tflite::InterpreterBuilder(*itsModel, resolver)(&itsInterpreter);
122 std::unique_ptr<edgetpu_device,
decltype(&edgetpu_free_devices)>
123 devices(edgetpu_list_devices(&num_devices), &edgetpu_free_devices);
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.");
129 auto const & device = devices.get()[tn];
131 ModifyGraphWithDelegate(std::unique_ptr<TfLiteDelegate,
decltype(&edgetpu_free_delegate)>
132 (edgetpu_create_delegate(device.type, device.path,
nullptr, 0), &edgetpu_free_delegate));
134 itsInterpreter->SetNumThreads(1);
136 if (itsInterpreter->AllocateTensors() != kTfLiteOk)
LFATAL(
"Failed to allocate tensors");
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));
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 <<
", "
148 << itsInterpreter->tensor(i)->bytes <<
" bytes, scale: "
149 << itsInterpreter->tensor(i)->params.scale <<
", zero: "
150 << itsInterpreter->tensor(i)->params.zero_point);
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]);
158 catch (std::exception
const & e)
160 std::string err =
"\n";
161 for (std::string
const & s : itsErrorReporter.itsErrors) err +=
"ERR " + s +
"\n";
163 throw std::runtime_error(err);
169 std::vector<std::string> & info)
171 if ( ! itsInterpreter)
LFATAL(
"Internal inconsistency");
173 if (blobs.size() != itsInterpreter->inputs().size())
174 LFATAL(
"Received " << blobs.size() <<
" input tensors, but network wants " << itsInterpreter->inputs().size());
176 auto const & input_indices = itsInterpreter->inputs();
177 for (
size_t b = 0; b < blobs.size(); ++b)
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);
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])
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?");
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");
205 if (itsInterpreter->Invoke() != kTfLiteOk)
LFATAL(
"Failed to invoke interpreter");
206 info.emplace_back(
"- Network forward pass ok");
209 auto const & output_indices = itsInterpreter->outputs();
210 std::vector<cv::Mat> outs;
212 for (
size_t o = 0;
o < output_indices.size(); ++
o)
214 auto const * otensor = itsInterpreter->tensor(output_indices[
o]);
215 if (otensor ==
nullptr)
LFATAL(
"Network produced Null output tensor " <<
o);
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]; }
223 TfLiteType
const ot = otensor->type;
224 std::string
const otname = TfLiteTypeGetName(ot);
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);
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);
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);
283 info.emplace_back(
"- Copied " + otname +
" output tensor " + std::to_string(
o));
284 outs.emplace_back(cvout);
289 LFATAL(
"Output tensor " << otensor->name <<
" has unsupported type: " << otname);
296 size_t const tn = tpunum::get();
297 if ((jevois::frameNum() % 50) == 0)
300 info.emplace_back(
jevois::sformat(
"- TPU%zu temp %dC", tn, temp / 1000));
void load() override
Load from disk.
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
virtual ~NetworkTPU()
Destructor.
virtual std::vector< vsi_nn_tensor_attr_t > outputShapes() override
Get shapes of all output tensors.
virtual std::vector< vsi_nn_tensor_attr_t > inputShapes() override
Get shapes of all input tensors.
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 waitBeforeDestroy()
If network is currently loading, wait until that is done before destroying.
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.
#define LERROR(msg)
Convenience macro for users to print out console or syslog messages, ERROR level.
#define LINFO(msg)
Convenience macro for users to print out console or syslog messages, INFO level.
int tf2cv(TfLiteType t)
Convert from TensorFlow data type to OpenCV.
vsi_nn_tensor_attr_t tensorattr(TfLiteTensor const *t)
Get tensor shape and type attributes for a TensorFlow Lite tensor.
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...
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.
unsigned int cvBytesPerPix(unsigned int cvtype)
Return the number of bytes per pixel for a given OpenCV pixel type.
std::string getFileString(char const *fname, int skip=0)
Read one line from a file and return it as a string.
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
Create a string using printf style arguments.
std::filesystem::path absolutePath(std::filesystem::path const &root, std::filesystem::path const &path)
Compute an absolute path from two paths.