JeVois  1.22
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
NetworkHailo.C
Go to the documentation of this file.
1// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2//
3// JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2022 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/Util/Utils.H>
22#include <jevois/DNN/Utils.H>
23#include <jevois/Core/Engine.H>
24
25#include <hailo/hailort.h>
26#include <hailo/hailort.hpp>
27
28// This code modified from tapas/apps/native/detection/detection_app.cpp
29// Copyright (c) 2021-2022 Hailo Technologies Ltd. All rights reserved.
30// Distributed under the LGPL license (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt)
31
32#define HAILO_CHECK(obj, msg) do { if (!obj) LFATAL(msg << ": " << obj.status() << " [" << \
33 hailo_get_status_message(obj.status()) << ']'); } while (0)
34
35// ####################################################################################################
36jevois::dnn::NetworkHailo::NetworkHailo(std::string const & instance) :
37 jevois::dnn::Network(instance)
38{
39
40 /* Skip this test for now... The overlay need to be tuned up to actually bind to the hailo chip.
41
42#ifdef JEVOIS_PLATFORM
43 try { (void)jevois::getFileString("/proc/device-tree/reserved-memory/linux,hailo_cma/name"); }
44 catch (...)
45 {
46 LFATAL("Using the Hailo8 accelerator requires some extra Linux kernel configuration.\n\n"
47 "Please edit '/boot/env.txt' (you can use the editor from the Config tab of the JeVois-Pro GUI), "
48 "and towards the end, add 'hailo' to the space-separated list of overlays.\n\n"
49 "Then reboot and it should be ready to go.");
50 }
51#endif
52
53 */
54}
55
56// ####################################################################################################
57std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkHailo::inputShapes()
58{ return itsInAttrs; }
59
60// ####################################################################################################
61std::vector<hailo_vstream_info_t> jevois::dnn::NetworkHailo::inputInfos() const
62{
63 std::vector<hailo_vstream_info_t> ret;
64 for (auto const & vs : itsInStreams) ret.emplace_back(vs.get_info());
65 return ret;
66}
67
68// ####################################################################################################
69std::vector<vsi_nn_tensor_attr_t> jevois::dnn::NetworkHailo::outputShapes()
70{ return itsOutAttrs; }
71
72// ####################################################################################################
73std::vector<hailo_vstream_info_t> jevois::dnn::NetworkHailo::outputInfos() const
74{
75 std::vector<hailo_vstream_info_t> ret;
76 for (auto const & vs : itsOutStreams) ret.emplace_back(vs.get_info());
77 return ret;
78}
79
80// ####################################################################################################
82{
83 // If we are loading the network via load() running in a thread, wait until that is done before we destroy
84 // (base class jevois::dnn::Network handles this):
85 waitBeforeDestroy();
86}
87
88// ####################################################################################################
90{
91 dataroot::freeze(doit);
92 model::freeze(doit);
93 jevois::dnn::Network::freeze(doit); // base class parameters
94}
95
96// ####################################################################################################
98{
99 bool success = false; bool first_time = true;
100 do
101 {
102 // We can only load once...
103 if (itsDevice) LFATAL("Network already loaded... restart the module to load a new one.");
104
105 // Create a device and load the HEF network file:
106 auto dev = hailort::Device::create_pcie();
107 if (!dev) LFATAL("Failed to create PCIe device:" << dev.status());
108 itsDevice = dev.release();
109
110 // Load the network from HEF:
111 std::string const m = jevois::absolutePath(dataroot::get(), model::get());
112 LINFO("Loading HEF file " << m << " ...");
113 auto hef = hailort::Hef::create(m);
114 HAILO_CHECK(hef, "Failed to load HEF file " << m);
115
116 std::vector<std::string> ngn = hef->get_network_groups_names();
117 for (std::string const & n : ngn) LINFO("Network Group: " << n);
118 if (ngn.size() != 1) LERROR("More than one network groups in HEF -- USING FIRST ONE");
119
120 auto configure_params = hef->create_configure_params(HAILO_STREAM_INTERFACE_PCIE);
121 HAILO_CHECK(configure_params, "Could not configure params from HEF file " << m);
122
123 auto network_groups = itsDevice->configure(hef.value(), configure_params.value());
124 if (!network_groups)
125 {
126 // If we already tried a Hailo reset, we are toast:
127 if (first_time == false)
128 {
129 engine()->reportError("Hailo PCIe driver failure uncorrectable by soft reset -- Reloading JeVois module");
130 engine()->requestSetFormat(-1); // reload current module
131 HAILO_CHECK(network_groups, "Could not configure device"); // this will LFATAL()
132 }
133
134 // Try to reset the device and unload/reload the driver:
135 LERROR("Could not configure device: " << network_groups.status() << " [" <<
136 hailo_get_status_message(network_groups.status()) << "] -- RESETTING HAILO DRIVER");
137
138 // FIXME: looks like there may be a memory leak in the Hailo PCIe driver. It may not always deallocate all DMA
139 // coherent (CMA) memory. Try a reset:
140 itsDevice->reset(HAILO_RESET_DEVICE_MODE_FORCED_SOFT);
141 itsDevice.reset();
142
143 // Sometimes works, but can still sometimes run out of CMA memory after loading a bunch of nets...
144 (void)jevois::system("/usr/bin/systemctl stop jevoispro-fan");
145 (void)jevois::system("/usr/sbin/rmmod hailo_pci");
146 (void)jevois::system("/usr/sbin/modprobe hailo_pci");
147 (void)jevois::system("/usr/bin/systemctl start jevoispro-fan");
148
149 first_time = false; success = false;
150 continue; // try again from the beginning...
151 }
152
153 if (network_groups->empty()) LFATAL("HEF file " << m << " does not contain any network groups");
154 itsNetGroup = std::move(network_groups->at(0)); // use the first network group
155 success = true;
156
157 } while (success == false);
158
159 // Configure the input and output virtual streams:
160 constexpr bool QUANTIZED = true;
161 constexpr hailo_format_type_t FORMAT_TYPE = HAILO_FORMAT_TYPE_AUTO;
162
163 auto vstreams = hailort::VStreamsBuilder::create_vstreams(*itsNetGroup, QUANTIZED, FORMAT_TYPE);
164 HAILO_CHECK(vstreams, "Failed to create vstreams");
165
166 itsInStreams = std::move(vstreams->first);
167 itsOutStreams = std::move(vstreams->second);
168
169 // Activate the network group:
170 auto activated_network_group = itsNetGroup->activate();
171 HAILO_CHECK(activated_network_group, "Failed activating network group");
172 itsActiveNetGroup = activated_network_group.release();
173
174 // Get the input and output attributes, allocate some cv::Mat for the outputs:
175 for (auto const & vs : itsInStreams)
176 {
177 itsInAttrs.emplace_back(jevois::dnn::tensorattr(vs.get_info()));
178 auto const & attr = itsInAttrs.back();
179 LINFO("Input " << vs.name() << ": " << jevois::dnn::attrstr(attr));
180 }
181
182 for (auto const & vs : itsOutStreams)
183 {
184 itsOutAttrs.emplace_back(jevois::dnn::tensorattr(vs.get_info()));
185 auto const & attr = itsOutAttrs.back();
186 LINFO("Output " << vs.name() << ": " << jevois::dnn::attrstr(attr));
187 itsRawOutMats.emplace_back(jevois::dnn::attrmat(attr));
188 itsOutMats.emplace_back(cv::Mat(jevois::dnn::attrdims(attr), CV_32F));
189 }
190
191 // Turbo parameter may have been changed before or while we loaded, so set it here:
192 itsDevice->set_throttling_state(! turbo::get());
193}
194
195// ####################################################################################################
196void jevois::dnn::NetworkHailo::onParamChange(network::turbo const &, bool const & newval)
197{
198 if (itsDevice) itsDevice->set_throttling_state(! newval);
199}
200
201// ####################################################################################################
202std::vector<cv::Mat> jevois::dnn::NetworkHailo::doprocess(std::vector<cv::Mat> const & blobs,
203 std::vector<std::string> & info)
204{
205 if (blobs.size() != itsInStreams.size())
206 LFATAL("Received " << blobs.size() << " blobs, but network has " << itsInStreams.size() << " inputs");
207
208 std::string err;
209 for (size_t i = 0; i < blobs.size(); ++i)
210 if (jevois::dnn::attrmatch(itsInAttrs[i], blobs[i]) == false)
211 err += "Input " + std::to_string(i) + ": received " + jevois::dnn::shapestr(blobs[i]) +
212 " but want: " + jevois::dnn::shapestr(itsInAttrs[i]) + "\n";
213 if (err.empty() == false) LFATAL(err);
214
215 // Launch the output reader threads (device->host) first:
216 std::vector<std::future<std::string>> fvec(itsInStreams.size() + itsOutStreams.size());
217 bool const dq = dequant::get();
218
219 for (uint32_t i = 0; i < itsOutStreams.size(); ++i)
220 fvec[i + itsInStreams.size()] = jevois::async([this](uint32_t i, bool dq) -> std::string
221 {
222 auto const & attr = itsOutAttrs[i];
223 uint8_t * tensor_data = (uint8_t *)itsRawOutMats[i].data;
224 size_t const sz = itsRawOutMats[i].total() * itsRawOutMats[i].elemSize();
225
226 auto status = itsOutStreams[i].read(hailort::MemoryView(tensor_data, sz));
227 if (status != HAILO_SUCCESS) LFATAL("Failed to collect output " << i << " from device: " << status);
228
229 if (dq)
230 {
231 itsOutMats[i] = jevois::dnn::dequantize(itsRawOutMats[i], attr);
232 return "- Out " + std::to_string(i) + ": " + jevois::dnn::attrstr(attr) + " -> 32F";
233 }
234 else
235 {
236 itsOutMats[i] = itsRawOutMats[i];
237 return "- Out " + std::to_string(i) + ": " + jevois::dnn::attrstr(attr);
238 }
239
240 }, i, dq);
241
242 // Launch the input writing (host->device) threads:
243 for (size_t b = 0; b < blobs.size(); ++b)
244 fvec[b] = jevois::async([this, &blobs](size_t b) -> std::string
245 {
246 cv::Mat const & blob = blobs[b];
247 size_t const sz = blob.total() * blob.elemSize();
248
249 // Copy blob data to device:
250 auto status = itsInStreams[b].write(hailort::MemoryView(blob.data, sz));
251 if (status != HAILO_SUCCESS) LFATAL("Failed to write input " << b << " data to device: " << status);
252
253 return "- In " + std::to_string(b) + ": " + jevois::dnn::attrstr(itsInAttrs[b]);
254 }, b);
255
256 // Add some info, once in a while:
257 static std::string devstr = "- Hailo8: ---W, ---C";
258 if ((jevois::frameNum() % 30) == 0)
259 {
260 bool throttle = itsDevice->get_throttling_state().value();
261 hailo_chip_temperature_info_t const temp = itsDevice->get_chip_temperature().value();
262 float pwr = itsDevice->power_measurement(HAILO_DVM_OPTIONS_AUTO, HAILO_POWER_MEASUREMENT_TYPES__POWER).value();
263
264 devstr = jevois::sformat("- Hailo8: %.1fW, %.0fC%s", pwr, temp.ts0_temperature, throttle ? "" : " (turbo)");
265 }
266
267 // Join all threads (may throw a single combined exception):
268 std::vector<std::string> retvec = jevois::joinall(fvec);
269 info.insert(info.end(), std::make_move_iterator(retvec.begin()), std::make_move_iterator(retvec.end()));
270 info.emplace_back(devstr);
271
272 return itsOutMats;
273}
274
275#endif // JEVOIS_PRO
#define success()
#define HAILO_CHECK(obj, msg)
void requestSetFormat(int idx)
Use this to request a format change from within process()
Definition Engine.C:942
void reportError(std::string const &err)
Definition Engine.C:1401
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 load() override
Load from disk.
std::vector< hailo_vstream_info_t > inputInfos() const
Hailo-only get input stream infos.
virtual ~NetworkHailo()
Destructor.
void onParamChange(network::turbo const &par, bool const &newval) override
void freeze(bool doit) override
Freeze/unfreeze parameters that users should not change while running.
virtual std::vector< vsi_nn_tensor_attr_t > inputShapes() override
Get shapes of all input tensors.
NetworkHailo(std::string const &instance)
Constructor.
virtual std::vector< vsi_nn_tensor_attr_t > outputShapes() override
Get shapes of all output tensors.
std::vector< hailo_vstream_info_t > outputInfos() const
Hailo-only get input stream infos, can be used to post-process quantized outputs.
Abstract class to represent a neural network.
Definition Network.H:212
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
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::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
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:495
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:871
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
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:502
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:785
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::string system(std::string const &cmd, bool errtoo=true)
Execute a command and grab stdout output to a string.
Definition Utils.C:462
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
Main namespace for all JeVois classes and functions.
Definition Concepts.dox:2